@aquera/nile-visualization 0.6.0 → 0.7.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/dist/src/internal/types/nile-chart-config-input.type.d.ts +6 -2
- package/dist/src/nile-chart/nile-chart-config-builder.js +2 -1
- package/dist/src/nile-chart/nile-chart.css.js +20 -6
- package/dist/src/nile-chart/nile-chart.js +7 -3
- package/dist/src/nile-kpi-chart/nile-kpi-chart.css.js +19 -11
- package/dist/src/nile-kpi-chart/nile-kpi-chart.d.ts +8 -2
- package/dist/src/nile-kpi-chart/nile-kpi-chart.js +43 -9
- package/package.json +1 -1
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import type { ChartConfigType } from './chart-config.type.js';
|
|
2
2
|
import type { AqConfigType } from './aq-config.type.js';
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import type { NileChartConfigBase } from '../../nile-chart/nile-chart-config.js';
|
|
4
|
+
/**
|
|
5
|
+
* Input to nileChartConfig(). Discriminated on chart.type.
|
|
6
|
+
* Card-level fields may sit on chart, in aq, or on the root (merged: root, then chart, then aq).
|
|
7
|
+
*/
|
|
8
|
+
export interface NileChartConfigInputType extends Partial<NileChartConfigBase> {
|
|
5
9
|
chart: ChartConfigType;
|
|
6
10
|
aq?: AqConfigType;
|
|
7
11
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/** Merges separated chart + aq config into a flat NileChartConfig. */
|
|
2
2
|
export function nileChartConfig(input) {
|
|
3
|
-
const { chart, aq } = input;
|
|
3
|
+
const { chart, aq, ...fromRoot } = input;
|
|
4
4
|
const { ai, switchable, ...rest } = aq ?? {};
|
|
5
5
|
return {
|
|
6
|
+
...fromRoot,
|
|
6
7
|
...chart,
|
|
7
8
|
...rest,
|
|
8
9
|
...(ai && { ai }),
|
|
@@ -19,7 +19,6 @@ export const styles = css `
|
|
|
19
19
|
box-shadow: var(--nile-box-shadow-3, var(--ng-shadow-sm));
|
|
20
20
|
transition: box-shadow var(--nile-transition-duration-default, var(--ng-transition-duration-default)) ease;
|
|
21
21
|
}
|
|
22
|
-
|
|
23
22
|
.chart-card:hover {
|
|
24
23
|
box-shadow: var(--nile-box-shadow-7, var(--ng-shadow-md));
|
|
25
24
|
}
|
|
@@ -28,8 +27,10 @@ export const styles = css `
|
|
|
28
27
|
|
|
29
28
|
.chart-header {
|
|
30
29
|
display: flex;
|
|
30
|
+
flex-direction: row;
|
|
31
31
|
align-items: center;
|
|
32
32
|
justify-content: space-between;
|
|
33
|
+
gap: var(--nile-spacing-lg, var(--ng-spacing-lg));
|
|
33
34
|
position: relative;
|
|
34
35
|
z-index: 10;
|
|
35
36
|
padding: var(--nile-spacing-2xl, var(--ng-spacing-2xl)) var(--nile-spacing-3xl, var(--ng-spacing-3xl)) var(--nile-spacing-xl, var(--ng-spacing-xl));
|
|
@@ -38,16 +39,24 @@ export const styles = css `
|
|
|
38
39
|
border-radius: var(--nile-radius-radius-3xl, var(--ng-radius-xl)) var(--nile-radius-radius-3xl, var(--ng-radius-xl)) 0 0;
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
.chart-header.chart-header--compact {
|
|
43
|
+
padding: var(--nile-spacing-xl, var(--ng-spacing-xl)) var(--nile-spacing-3xl, var(--ng-spacing-3xl)) var(--nile-spacing-lg, var(--ng-spacing-lg));
|
|
44
|
+
}
|
|
45
|
+
|
|
41
46
|
.chart-header-titles {
|
|
47
|
+
display: flex;
|
|
48
|
+
flex-direction: column;
|
|
49
|
+
align-items: flex-start;
|
|
50
|
+
justify-content: center;
|
|
42
51
|
min-width: 0;
|
|
43
|
-
flex: 1;
|
|
52
|
+
flex: 1 1 auto;
|
|
44
53
|
}
|
|
45
54
|
|
|
46
55
|
.chart-header-title {
|
|
47
56
|
margin: 0;
|
|
48
57
|
font-family: var(--nile-font-family-serif-colfax-medium, var(--ng-font-family-display));
|
|
49
|
-
font-size: var(--nile-type-scale-6, var(--ng-font-size-text-
|
|
50
|
-
|
|
58
|
+
font-size: var(--nile-type-scale-6, var(--ng-font-size-text-l));
|
|
59
|
+
font-weight: var(--nile-font-weight-semi-bold, var(--ng-font-weight-semibold));
|
|
51
60
|
color: var(--nile-colors-dark-900, var(--ng-colors-text-primary-900));
|
|
52
61
|
line-height: 1.3;
|
|
53
62
|
}
|
|
@@ -66,7 +75,7 @@ export const styles = css `
|
|
|
66
75
|
align-items: center;
|
|
67
76
|
gap: var(--nile-spacing-xs, var(--ng-spacing-xs));
|
|
68
77
|
flex-shrink: 0;
|
|
69
|
-
|
|
78
|
+
align-self: center;
|
|
70
79
|
}
|
|
71
80
|
|
|
72
81
|
/* ── Card Body ── */
|
|
@@ -287,7 +296,12 @@ export const styles = css `
|
|
|
287
296
|
background: transparent;
|
|
288
297
|
color: var(--nile-colors-primary-600, var(--ng-colors-fg-brand-primary-600));
|
|
289
298
|
}
|
|
290
|
-
|
|
299
|
+
.chart-inner--kpi {
|
|
300
|
+
overflow-x: hidden;
|
|
301
|
+
overflow-y: auto;
|
|
302
|
+
-webkit-overflow-scrolling: touch;
|
|
303
|
+
contain: none;
|
|
304
|
+
}
|
|
291
305
|
.ai-trigger.active {
|
|
292
306
|
background: var(--nile-colors-primary-100, var(--ng-colors-bg-brand-primary));
|
|
293
307
|
color: var(--nile-colors-primary-700, var(--ng-colors-bg-brand-solid-hover));
|
|
@@ -329,13 +329,14 @@ let NileChart = class NileChart extends NileElement {
|
|
|
329
329
|
const title = this.headerTitle;
|
|
330
330
|
const subtitle = this.headerSubtitle;
|
|
331
331
|
const showDefaultTitles = !this.hasHeaderSlotContent && !!(title || subtitle);
|
|
332
|
+
const headerCompact = !subtitle?.trim();
|
|
332
333
|
return html `
|
|
333
|
-
<div class="chart-header">
|
|
334
|
+
<div class="chart-header ${headerCompact ? 'chart-header--compact' : ''}">
|
|
334
335
|
<div class="chart-header-titles">
|
|
335
336
|
<slot name="header" @slotchange=${this.onHeaderSlotChange}></slot>
|
|
336
337
|
${showDefaultTitles
|
|
337
338
|
? html `
|
|
338
|
-
${title ? html `<
|
|
339
|
+
${title ? html `<p class="chart-header-title">${title}</p>` : nothing}
|
|
339
340
|
${subtitle ? html `<p class="chart-header-subtitle">${subtitle}</p>` : nothing}
|
|
340
341
|
`
|
|
341
342
|
: nothing}
|
|
@@ -851,6 +852,7 @@ let NileChart = class NileChart extends NileElement {
|
|
|
851
852
|
case 'kpi': {
|
|
852
853
|
const k = config;
|
|
853
854
|
return html `<nile-kpi-chart
|
|
855
|
+
embed-in-nile-chart
|
|
854
856
|
.config=${{
|
|
855
857
|
chart: {
|
|
856
858
|
type: 'kpi',
|
|
@@ -891,12 +893,14 @@ let NileChart = class NileChart extends NileElement {
|
|
|
891
893
|
${this.renderHeader()}
|
|
892
894
|
<div class="chart-wrapper">
|
|
893
895
|
<div class="chart-inner">
|
|
896
|
+
<div
|
|
897
|
+
class="chart-inner ${this.activeConfig?.type === 'kpi' ? 'chart-inner--kpi' : ''}"
|
|
898
|
+
>
|
|
894
899
|
${this.activeConfig ? this.renderChartContent() : html `<slot></slot>`}
|
|
895
900
|
${this.renderAiPanel()}
|
|
896
901
|
</div>
|
|
897
902
|
</div>
|
|
898
903
|
<slot name="footer"></slot>
|
|
899
|
-
<slot name="footer"></slot>
|
|
900
904
|
</div>
|
|
901
905
|
`;
|
|
902
906
|
}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { css } from 'lit';
|
|
2
2
|
export const styles = css `
|
|
3
3
|
:host {
|
|
4
|
-
display:
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
5
6
|
width: 100%;
|
|
6
7
|
position: relative;
|
|
8
|
+
box-sizing: border-box;
|
|
7
9
|
}
|
|
8
10
|
|
|
9
11
|
:host([hidden]) {
|
|
10
12
|
display: none;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
/*
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
/* Card / gauge chrome on the host when used alone (inside nile-chart, embed-in-nile-chart skips this). */
|
|
16
|
+
:host([variant='card']:not([embed-in-nile-chart])),
|
|
17
|
+
:host([variant='gauge']:not([embed-in-nile-chart])) {
|
|
16
18
|
background: transparent;
|
|
17
19
|
border: var(--nile-border-width-1, var(--ng-stroke-width-1)) solid var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));
|
|
18
20
|
border-radius: var(--nile-radius-radius-3xl, var(--ng-radius-xl));
|
|
@@ -20,13 +22,14 @@ export const styles = css `
|
|
|
20
22
|
transition: box-shadow var(--nile-transition-duration-default, var(--ng-transition-duration-default)) ease;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
:host([variant='card']:not([embed-in-nile-chart]):hover),
|
|
26
|
+
:host([variant='gauge']:not([embed-in-nile-chart]):hover) {
|
|
24
27
|
box-shadow: var(--nile-box-shadow-7, var(--ng-shadow-md));
|
|
25
28
|
}
|
|
26
29
|
|
|
27
|
-
/* ── Base layout ── */
|
|
28
|
-
|
|
29
30
|
.kpi {
|
|
31
|
+
flex: 1 1 auto;
|
|
32
|
+
|
|
30
33
|
display: flex;
|
|
31
34
|
flex-direction: column;
|
|
32
35
|
gap: var(--nile-spacing-md, var(--ng-spacing-md));
|
|
@@ -52,7 +55,7 @@ export const styles = css `
|
|
|
52
55
|
.kpi-value {
|
|
53
56
|
margin: 0;
|
|
54
57
|
font-family: var(--nile-font-family-serif-colfax-medium, var(--ng-font-family-display));
|
|
55
|
-
font-size: 36px;
|
|
58
|
+
font-size: clamp(1.25rem, 2.5vw + 0.75rem, 36px);
|
|
56
59
|
font-weight: var(--nile-font-weight-semi-bold, var(--ng-font-weight-semibold));
|
|
57
60
|
color: var(--nile-colors-dark-900, var(--ng-colors-text-primary-900));
|
|
58
61
|
line-height: 1.2;
|
|
@@ -115,7 +118,8 @@ export const styles = css `
|
|
|
115
118
|
|
|
116
119
|
.kpi-sparkline {
|
|
117
120
|
width: 100%;
|
|
118
|
-
|
|
121
|
+
flex: 0 1 48px;
|
|
122
|
+
min-height: 22px;
|
|
119
123
|
margin-top: var(--nile-spacing-xs, var(--ng-spacing-xs));
|
|
120
124
|
}
|
|
121
125
|
|
|
@@ -127,8 +131,11 @@ export const styles = css `
|
|
|
127
131
|
}
|
|
128
132
|
|
|
129
133
|
.kpi-gauge-container {
|
|
130
|
-
width:
|
|
131
|
-
|
|
134
|
+
width: 100%;
|
|
135
|
+
max-width: 160px;
|
|
136
|
+
aspect-ratio: 1;
|
|
137
|
+
flex: 0 1 160px;
|
|
138
|
+
min-width: 72px;
|
|
132
139
|
margin: 0 auto;
|
|
133
140
|
}
|
|
134
141
|
|
|
@@ -153,6 +160,7 @@ export const styles = css `
|
|
|
153
160
|
/* ── Loading state ── */
|
|
154
161
|
|
|
155
162
|
.chart-loading {
|
|
163
|
+
flex: 1 1 auto;
|
|
156
164
|
display: flex;
|
|
157
165
|
align-items: center;
|
|
158
166
|
justify-content: center;
|
|
@@ -24,8 +24,8 @@ export interface ChartKpiSeparatedPayload {
|
|
|
24
24
|
gaugeColor?: string;
|
|
25
25
|
loading?: boolean;
|
|
26
26
|
options?: Highcharts.Options;
|
|
27
|
-
/**
|
|
28
|
-
height?: string;
|
|
27
|
+
/** Box size when a height is set (host min-height and height). */
|
|
28
|
+
height?: string | number;
|
|
29
29
|
}
|
|
30
30
|
/** Separated `{ chart, aq }` input for `<nile-kpi-chart>`. */
|
|
31
31
|
export interface NileKpiConfigInputType {
|
|
@@ -89,12 +89,18 @@ export declare class NileKpiChart extends NileElement {
|
|
|
89
89
|
loading: boolean;
|
|
90
90
|
/** Highcharts options override for the sparkline or gauge. */
|
|
91
91
|
options: Highcharts.Options;
|
|
92
|
+
/**
|
|
93
|
+
* Set by nile-chart: skip host border/shadow (variant card/gauge) so the parent chart-card is the only frame.
|
|
94
|
+
*/
|
|
95
|
+
embedInNileChart: boolean;
|
|
96
|
+
private formatCssLength;
|
|
92
97
|
/** Apply `{ chart, aq }` to individual properties. */
|
|
93
98
|
private applyConfig;
|
|
94
99
|
connectedCallback(): void;
|
|
95
100
|
disconnectedCallback(): void;
|
|
96
101
|
protected firstUpdated(): void;
|
|
97
102
|
protected updated(changedProperties: PropertyValues): void;
|
|
103
|
+
private syncSparklineChartSize;
|
|
98
104
|
private setupResizeObserver;
|
|
99
105
|
private buildSparklineOptions;
|
|
100
106
|
private buildGaugeOptions;
|
|
@@ -47,6 +47,23 @@ let NileKpiChart = class NileKpiChart extends NileElement {
|
|
|
47
47
|
this.loading = false;
|
|
48
48
|
/** Highcharts options override for the sparkline or gauge. */
|
|
49
49
|
this.options = {};
|
|
50
|
+
/**
|
|
51
|
+
* Set by nile-chart: skip host border/shadow (variant card/gauge) so the parent chart-card is the only frame.
|
|
52
|
+
*/
|
|
53
|
+
this.embedInNileChart = false;
|
|
54
|
+
}
|
|
55
|
+
formatCssLength(value) {
|
|
56
|
+
if (value == null)
|
|
57
|
+
return null;
|
|
58
|
+
if (typeof value === 'number') {
|
|
59
|
+
return Number.isFinite(value) ? `${value}px` : null;
|
|
60
|
+
}
|
|
61
|
+
const s = String(value).trim();
|
|
62
|
+
if (!s)
|
|
63
|
+
return null;
|
|
64
|
+
if (/^-?\d*\.?\d+$/.test(s))
|
|
65
|
+
return `${s}px`;
|
|
66
|
+
return s;
|
|
50
67
|
}
|
|
51
68
|
/** Apply `{ chart, aq }` to individual properties. */
|
|
52
69
|
applyConfig(cfg) {
|
|
@@ -86,11 +103,19 @@ let NileKpiChart = class NileKpiChart extends NileElement {
|
|
|
86
103
|
this.loading = c.loading;
|
|
87
104
|
if (c.options !== undefined)
|
|
88
105
|
this.options = c.options;
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
106
|
+
if ('height' in c) {
|
|
107
|
+
const h = this.formatCssLength(c.height);
|
|
108
|
+
if (h) {
|
|
109
|
+
/* min-height only: host can grow with content; sparkline shrinks via flex + setSize */
|
|
110
|
+
this.style.minHeight = h;
|
|
111
|
+
this.style.removeProperty('height');
|
|
112
|
+
this.style.removeProperty('max-height');
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
93
115
|
this.style.removeProperty('min-height');
|
|
116
|
+
this.style.removeProperty('height');
|
|
117
|
+
this.style.removeProperty('max-height');
|
|
118
|
+
}
|
|
94
119
|
}
|
|
95
120
|
}
|
|
96
121
|
if (aq) {
|
|
@@ -125,6 +150,7 @@ let NileKpiChart = class NileKpiChart extends NileElement {
|
|
|
125
150
|
if (sparklineProps.some(p => changedProperties.has(p))) {
|
|
126
151
|
if (this.sparklineChart) {
|
|
127
152
|
this.sparklineChart.update(this.buildSparklineOptions(), true, true);
|
|
153
|
+
requestAnimationFrame(() => this.syncSparklineChartSize());
|
|
128
154
|
}
|
|
129
155
|
else {
|
|
130
156
|
this.initSparkline();
|
|
@@ -140,9 +166,16 @@ let NileKpiChart = class NileKpiChart extends NileElement {
|
|
|
140
166
|
}
|
|
141
167
|
}
|
|
142
168
|
}
|
|
169
|
+
syncSparklineChartSize() {
|
|
170
|
+
if (!this.sparklineChart || !this.sparklineContainer)
|
|
171
|
+
return;
|
|
172
|
+
const rect = this.sparklineContainer.getBoundingClientRect();
|
|
173
|
+
const h = Math.max(22, Math.round(rect.height));
|
|
174
|
+
this.sparklineChart.setSize(null, h, false);
|
|
175
|
+
}
|
|
143
176
|
setupResizeObserver() {
|
|
144
177
|
this.resizeObserver = new ResizeObserver(() => {
|
|
145
|
-
this.
|
|
178
|
+
this.syncSparklineChartSize();
|
|
146
179
|
this.gaugeChart?.reflow();
|
|
147
180
|
});
|
|
148
181
|
if (this.sparklineContainer)
|
|
@@ -252,6 +285,7 @@ let NileKpiChart = class NileKpiChart extends NileElement {
|
|
|
252
285
|
this._hc = await getHighcharts();
|
|
253
286
|
this.destroySparkline();
|
|
254
287
|
this.sparklineChart = this._hc.chart(this.sparklineContainer, this.buildSparklineOptions());
|
|
288
|
+
requestAnimationFrame(() => this.syncSparklineChartSize());
|
|
255
289
|
this.emit('nile-chart-ready', { chart: this.sparklineChart });
|
|
256
290
|
}
|
|
257
291
|
async initGauge() {
|
|
@@ -326,10 +360,7 @@ let NileKpiChart = class NileKpiChart extends NileElement {
|
|
|
326
360
|
if (this.loading) {
|
|
327
361
|
return html `<div class="chart-loading">Loading...</div>`;
|
|
328
362
|
}
|
|
329
|
-
|
|
330
|
-
if (useCard) {
|
|
331
|
-
return html `<div class="kpi-card">${this.renderContent()}</div>`;
|
|
332
|
-
}
|
|
363
|
+
/* Same DOM as inside nile-chart: one surface; card/gauge chrome is on :host when standalone. */
|
|
333
364
|
return this.renderContent();
|
|
334
365
|
}
|
|
335
366
|
};
|
|
@@ -394,6 +425,9 @@ __decorate([
|
|
|
394
425
|
__decorate([
|
|
395
426
|
property({ type: Object })
|
|
396
427
|
], NileKpiChart.prototype, "options", void 0);
|
|
428
|
+
__decorate([
|
|
429
|
+
property({ type: Boolean, reflect: true, attribute: 'embed-in-nile-chart' })
|
|
430
|
+
], NileKpiChart.prototype, "embedInNileChart", void 0);
|
|
397
431
|
NileKpiChart = __decorate([
|
|
398
432
|
customElement('nile-kpi-chart')
|
|
399
433
|
], NileKpiChart);
|