@finos/legend-lego 2.0.194 → 2.0.195

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/lib/index.css +2 -2
  2. package/lib/index.css.map +1 -1
  3. package/lib/legend-ai/LegendAITypes.d.ts +0 -33
  4. package/lib/legend-ai/LegendAITypes.d.ts.map +1 -1
  5. package/lib/legend-ai/LegendAITypes.js +1 -39
  6. package/lib/legend-ai/LegendAITypes.js.map +1 -1
  7. package/lib/legend-ai/LegendAI_LegendApplicationPlugin_Extension.d.ts +1 -96
  8. package/lib/legend-ai/LegendAI_LegendApplicationPlugin_Extension.d.ts.map +1 -1
  9. package/lib/legend-ai/LegendAI_LegendApplicationPlugin_Extension.js +0 -56
  10. package/lib/legend-ai/LegendAI_LegendApplicationPlugin_Extension.js.map +1 -1
  11. package/lib/legend-ai/__test-utils__/LegendAITestUtils.d.ts.map +1 -1
  12. package/lib/legend-ai/__test-utils__/LegendAITestUtils.js +0 -6
  13. package/lib/legend-ai/__test-utils__/LegendAITestUtils.js.map +1 -1
  14. package/lib/legend-ai/components/LegendAIChat.d.ts +1 -2
  15. package/lib/legend-ai/components/LegendAIChat.d.ts.map +1 -1
  16. package/lib/legend-ai/components/LegendAIChat.js +10 -14
  17. package/lib/legend-ai/components/LegendAIChat.js.map +1 -1
  18. package/lib/legend-ai/index.d.ts +2 -5
  19. package/lib/legend-ai/index.d.ts.map +1 -1
  20. package/lib/legend-ai/index.js +2 -5
  21. package/lib/legend-ai/index.js.map +1 -1
  22. package/lib/legend-ai/stores/LegendAIChatState.d.ts +5 -12
  23. package/lib/legend-ai/stores/LegendAIChatState.d.ts.map +1 -1
  24. package/lib/legend-ai/stores/LegendAIChatState.js +69 -604
  25. package/lib/legend-ai/stores/LegendAIChatState.js.map +1 -1
  26. package/package.json +5 -5
  27. package/src/legend-ai/LegendAITypes.ts +1 -51
  28. package/src/legend-ai/LegendAI_LegendApplicationPlugin_Extension.ts +0 -169
  29. package/src/legend-ai/__test-utils__/LegendAITestUtils.ts +0 -9
  30. package/src/legend-ai/components/LegendAIChat.tsx +26 -74
  31. package/src/legend-ai/index.ts +0 -18
  32. package/src/legend-ai/stores/LegendAIChatState.ts +128 -1039
  33. package/tsconfig.json +0 -3
  34. package/lib/legend-ai/components/LegendAIAnalysisPanel.d.ts +0 -24
  35. package/lib/legend-ai/components/LegendAIAnalysisPanel.d.ts.map +0 -1
  36. package/lib/legend-ai/components/LegendAIAnalysisPanel.js +0 -35
  37. package/lib/legend-ai/components/LegendAIAnalysisPanel.js.map +0 -1
  38. package/lib/legend-ai/components/LegendAIAnalysisUtils.d.ts +0 -23
  39. package/lib/legend-ai/components/LegendAIAnalysisUtils.d.ts.map +0 -1
  40. package/lib/legend-ai/components/LegendAIAnalysisUtils.js +0 -168
  41. package/lib/legend-ai/components/LegendAIAnalysisUtils.js.map +0 -1
  42. package/lib/legend-ai/components/LegendAICharts.d.ts +0 -25
  43. package/lib/legend-ai/components/LegendAICharts.d.ts.map +0 -1
  44. package/lib/legend-ai/components/LegendAICharts.js +0 -70
  45. package/lib/legend-ai/components/LegendAICharts.js.map +0 -1
  46. package/src/legend-ai/components/LegendAIAnalysisPanel.tsx +0 -102
  47. package/src/legend-ai/components/LegendAIAnalysisUtils.ts +0 -226
  48. package/src/legend-ai/components/LegendAICharts.tsx +0 -166
@@ -1,226 +0,0 @@
1
- /**
2
- * Copyright (c) 2026-present, Goldman Sachs
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
-
17
- import {
18
- type LegendAIKeyMetric,
19
- type LegendAIChartDataPoint,
20
- LegendAIChartType,
21
- } from '../LegendAI_LegendApplicationPlugin_Extension.js';
22
- import type { LegendAIGridData } from '../LegendAITypes.js';
23
- import { isNonNullable, isNumber, isString } from '@finos/legend-shared';
24
-
25
- const CHART_PALETTE_COUNT = 10;
26
- const MAX_CHART_ITEMS = 10;
27
- const TOP_N_ITEMS = 5;
28
- const MAX_PROFILE_SAMPLE = 1000;
29
- const MAX_KEY_METRICS = 4;
30
- const MAX_METRIC_COLUMNS = 5;
31
- const MAX_BAR_CHART_ROWS = 20;
32
- const MAX_PIE_CHART_ROWS = 6;
33
-
34
- interface ColumnProfile {
35
- name: string;
36
- isNumeric: boolean;
37
- isString: boolean;
38
- uniqueCount: number;
39
- values: unknown[];
40
- numericValues: number[];
41
- }
42
-
43
- function profileColumns(gridData: LegendAIGridData): ColumnProfile[] {
44
- const rows =
45
- gridData.rowData.length > MAX_PROFILE_SAMPLE
46
- ? gridData.rowData.slice(0, MAX_PROFILE_SAMPLE)
47
- : gridData.rowData;
48
-
49
- return gridData.columnDefs.map((col) => {
50
- const field = col.field ?? col.colId ?? '';
51
- const values = rows.map((r) => r[field]).filter(isNonNullable);
52
- const numericValues = values.filter(isNumber);
53
- const unique = new Set(values.map(String));
54
- return {
55
- name: field,
56
- isNumeric: numericValues.length === values.length && values.length > 0,
57
- isString: values.length > 0 && values.every(isString),
58
- uniqueCount: unique.size,
59
- values,
60
- numericValues,
61
- };
62
- });
63
- }
64
-
65
- function formatNumber(n: number): string {
66
- if (Number.isInteger(n) && Math.abs(n) < 1_000_000) {
67
- return n.toLocaleString();
68
- }
69
- if (Math.abs(n) >= 1_000_000) {
70
- return `${(n / 1_000_000).toFixed(1)}M`;
71
- }
72
- if (Math.abs(n) >= 1_000) {
73
- return `${(n / 1_000).toFixed(1)}K`;
74
- }
75
- return n.toFixed(2);
76
- }
77
-
78
- function computeNumericMetrics(
79
- col: ColumnProfile,
80
- metrics: LegendAIKeyMetric[],
81
- ): void {
82
- let sum = 0;
83
- let min = Number.POSITIVE_INFINITY;
84
- let max = Number.NEGATIVE_INFINITY;
85
- for (const n of col.numericValues) {
86
- sum += n;
87
- if (n < min) {
88
- min = n;
89
- }
90
- if (n > max) {
91
- max = n;
92
- }
93
- }
94
- const avg = sum / col.numericValues.length;
95
-
96
- if (col.uniqueCount > 1) {
97
- metrics.push({
98
- label: `Avg ${col.name}`,
99
- value: formatNumber(avg),
100
- ...(min === max
101
- ? {}
102
- : { detail: `${formatNumber(min)} – ${formatNumber(max)}` }),
103
- });
104
- }
105
-
106
- if (col.uniqueCount > 2 && metrics.length < MAX_METRIC_COLUMNS) {
107
- metrics.push({
108
- label: `Total ${col.name}`,
109
- value: formatNumber(sum),
110
- });
111
- }
112
- }
113
-
114
- export function computeKeyMetrics(
115
- gridData: LegendAIGridData,
116
- ): LegendAIKeyMetric[] {
117
- const profiles = profileColumns(gridData);
118
- const metrics: LegendAIKeyMetric[] = [];
119
- const rowCount = gridData.rowData.length;
120
-
121
- metrics.push({
122
- label: 'Total Rows',
123
- value: rowCount.toLocaleString(),
124
- });
125
-
126
- const numericCol = profiles.find(
127
- (c) => c.isNumeric && c.numericValues.length > 0,
128
- );
129
- if (numericCol) {
130
- computeNumericMetrics(numericCol, metrics);
131
- }
132
-
133
- const stringCol = profiles.find(
134
- (c) => c.isString && c.uniqueCount > 1 && c.uniqueCount <= rowCount,
135
- );
136
- if (stringCol) {
137
- metrics.push({
138
- label: `Unique ${stringCol.name}`,
139
- value: stringCol.uniqueCount.toLocaleString(),
140
- });
141
- }
142
-
143
- return metrics.slice(0, MAX_KEY_METRICS);
144
- }
145
-
146
- export function inferChartType(gridData: LegendAIGridData): LegendAIChartType {
147
- const profiles = profileColumns(gridData);
148
- const numericCols = profiles.filter((c) => c.isNumeric);
149
- const stringCols = profiles.filter((c) => c.isString && c.uniqueCount > 1);
150
-
151
- if (
152
- stringCols.length >= 1 &&
153
- numericCols.length >= 1 &&
154
- gridData.rowData.length <= MAX_BAR_CHART_ROWS
155
- ) {
156
- if (gridData.rowData.length <= MAX_PIE_CHART_ROWS) {
157
- return LegendAIChartType.PIE;
158
- }
159
- return LegendAIChartType.BAR;
160
- }
161
-
162
- if (numericCols.length >= 1 && gridData.rowData.length > 1) {
163
- return LegendAIChartType.BAR;
164
- }
165
-
166
- return LegendAIChartType.NONE;
167
- }
168
-
169
- export function computeChartData(
170
- gridData: LegendAIGridData,
171
- ): LegendAIChartDataPoint[] {
172
- const profiles = profileColumns(gridData);
173
- const numericCol = profiles.find((c) => c.isNumeric);
174
- const labelCol = profiles.find((c) => c.isString && c.uniqueCount > 1);
175
-
176
- if (!numericCol) {
177
- return [];
178
- }
179
-
180
- const field = numericCol.name;
181
- const labelField = labelCol?.name;
182
- const rows =
183
- gridData.rowData.length > MAX_PROFILE_SAMPLE
184
- ? gridData.rowData.slice(0, MAX_PROFILE_SAMPLE)
185
- : gridData.rowData;
186
-
187
- const entries = rows
188
- .map((row) => {
189
- const rawValue = row[field];
190
- return {
191
- label: labelField
192
- ? String(row[labelField] ?? '')
193
- : String(row[gridData.columnDefs[0]?.field ?? ''] ?? ''),
194
- value: typeof rawValue === 'number' ? rawValue : 0,
195
- };
196
- })
197
- .filter((e) => e.label.length > 0)
198
- .sort((a, b) => b.value - a.value)
199
- .slice(0, MAX_CHART_ITEMS);
200
-
201
- return entries.map((e, i) => ({
202
- label: e.label,
203
- value: e.value,
204
- colorIndex: i % CHART_PALETTE_COUNT,
205
- }));
206
- }
207
-
208
- export function computeTopItems(
209
- gridData: LegendAIGridData,
210
- ): LegendAIChartDataPoint[] {
211
- return computeChartData(gridData).slice(0, TOP_N_ITEMS);
212
- }
213
-
214
- export function findNumericColumnName(
215
- gridData: LegendAIGridData,
216
- ): string | undefined {
217
- const profiles = profileColumns(gridData);
218
- const numericCol = profiles.find((c) => c.isNumeric);
219
- if (!numericCol) {
220
- return undefined;
221
- }
222
- const colDef = gridData.columnDefs.find(
223
- (c) => (c.field ?? c.colId ?? '') === numericCol.name,
224
- );
225
- return colDef?.headerName ?? colDef?.field;
226
- }
@@ -1,166 +0,0 @@
1
- /**
2
- * Copyright (c) 2026-present, Goldman Sachs
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
-
17
- import { useMemo } from 'react';
18
- import type { LegendAIChartDataPoint } from '../LegendAI_LegendApplicationPlugin_Extension.js';
19
-
20
- const CHART_PALETTE_SIZE = 10;
21
- const DONUT_SIZE = 160;
22
- const DONUT_STROKE = 24;
23
- const DONUT_RADIUS = (DONUT_SIZE - DONUT_STROKE) / 2;
24
- const DONUT_CIRCUMFERENCE = 2 * Math.PI * DONUT_RADIUS;
25
-
26
- function getChartColor(index: number): string {
27
- return `var(--ai-chart-color-${(index % CHART_PALETTE_SIZE) + 1})`;
28
- }
29
-
30
- function resolveColor(item: LegendAIChartDataPoint, index: number): string {
31
- return item.color ?? getChartColor(item.colorIndex ?? index);
32
- }
33
-
34
- export const LegendAIBarChart = (props: {
35
- data: LegendAIChartDataPoint[];
36
- title?: string;
37
- }): React.ReactNode => {
38
- const { data, title } = props;
39
-
40
- const maxValue = useMemo(
41
- () => data.reduce((m, d) => Math.max(m, d.value), 0) || 1,
42
- [data],
43
- );
44
-
45
- if (data.length === 0) {
46
- return null;
47
- }
48
-
49
- return (
50
- <div className="legend-ai-chart legend-ai-chart--bar">
51
- {title !== undefined && title.length > 0 && (
52
- <div className="legend-ai-chart__title">{title}</div>
53
- )}
54
- <div className="legend-ai-chart__bars">
55
- {data.map((item, idx) => {
56
- const pct = (item.value / maxValue) * 100;
57
- return (
58
- <div key={item.label} className="legend-ai-chart__bar-row">
59
- <span className="legend-ai-chart__bar-label" title={item.label}>
60
- {item.label}
61
- </span>
62
- <div className="legend-ai-chart__bar-track">
63
- <div
64
- className="legend-ai-chart__bar-fill"
65
- style={{
66
- width: `${pct}%`,
67
- backgroundColor: resolveColor(item, idx),
68
- animationDelay: `${idx * 60}ms`,
69
- }}
70
- />
71
- </div>
72
- <span className="legend-ai-chart__bar-value">
73
- {Number.isInteger(item.value)
74
- ? item.value.toLocaleString()
75
- : item.value}
76
- </span>
77
- </div>
78
- );
79
- })}
80
- </div>
81
- </div>
82
- );
83
- };
84
-
85
- export const LegendAIDonutChart = (props: {
86
- data: LegendAIChartDataPoint[];
87
- title?: string;
88
- }): React.ReactNode => {
89
- const { data, title } = props;
90
-
91
- const total = useMemo(
92
- () => data.reduce((s, d) => s + d.value, 0) || 1,
93
- [data],
94
- );
95
-
96
- const segments = useMemo(() => {
97
- let offset = 0;
98
- return data.map((item) => {
99
- const pct = item.value / total;
100
- const dashLen = pct * DONUT_CIRCUMFERENCE;
101
- const seg = {
102
- ...item,
103
- dashLen,
104
- dashOffset: -offset,
105
- pct,
106
- };
107
- offset += dashLen;
108
- return seg;
109
- });
110
- }, [data, total]);
111
-
112
- if (data.length === 0) {
113
- return null;
114
- }
115
-
116
- const center = DONUT_SIZE / 2;
117
-
118
- return (
119
- <div className="legend-ai-chart legend-ai-chart--donut">
120
- {title !== undefined && title.length > 0 && (
121
- <div className="legend-ai-chart__title">{title}</div>
122
- )}
123
- <div className="legend-ai-chart__donut-wrapper">
124
- <svg
125
- viewBox={`0 0 ${DONUT_SIZE} ${DONUT_SIZE}`}
126
- className="legend-ai-chart__donut-svg"
127
- >
128
- {segments.map((seg, idx) => (
129
- <circle
130
- key={seg.label}
131
- cx={center}
132
- cy={center}
133
- r={DONUT_RADIUS}
134
- fill="none"
135
- stroke={resolveColor(seg, idx)}
136
- strokeWidth={DONUT_STROKE}
137
- strokeDasharray={`${seg.dashLen} ${DONUT_CIRCUMFERENCE - seg.dashLen}`}
138
- strokeDashoffset={seg.dashOffset}
139
- className="legend-ai-chart__donut-segment"
140
- />
141
- ))}
142
- </svg>
143
- <div className="legend-ai-chart__donut-center">
144
- <span className="legend-ai-chart__donut-total">
145
- {total.toLocaleString()}
146
- </span>
147
- <span className="legend-ai-chart__donut-total-label">total</span>
148
- </div>
149
- </div>
150
- <div className="legend-ai-chart__legend">
151
- {data.map((item, idx) => (
152
- <div key={item.label} className="legend-ai-chart__legend-item">
153
- <span
154
- className="legend-ai-chart__legend-dot"
155
- style={{ backgroundColor: resolveColor(item, idx) }}
156
- />
157
- <span className="legend-ai-chart__legend-label">{item.label}</span>
158
- <span className="legend-ai-chart__legend-value">
159
- {item.value.toLocaleString()}
160
- </span>
161
- </div>
162
- ))}
163
- </div>
164
- </div>
165
- );
166
- };