@allternit/viz 0.1.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.
Files changed (45) hide show
  1. package/README.md +217 -0
  2. package/dist/charts/canvas-renderer.d.ts +40 -0
  3. package/dist/charts/canvas-renderer.d.ts.bak +40 -0
  4. package/dist/charts/canvas-renderer.d.ts.map +1 -0
  5. package/dist/charts/canvas-renderer.js +580 -0
  6. package/dist/charts/canvas-renderer.js.bak +580 -0
  7. package/dist/charts/canvas-renderer.js.map +1 -0
  8. package/dist/charts/svg-renderer.d.ts +21 -0
  9. package/dist/charts/svg-renderer.d.ts.bak +21 -0
  10. package/dist/charts/svg-renderer.d.ts.map +1 -0
  11. package/dist/charts/svg-renderer.js +285 -0
  12. package/dist/charts/svg-renderer.js.bak +285 -0
  13. package/dist/charts/svg-renderer.js.map +1 -0
  14. package/dist/index.d.ts +34 -0
  15. package/dist/index.d.ts.bak +34 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +35 -0
  18. package/dist/index.js.bak +35 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/types.d.ts +342 -0
  21. package/dist/types.d.ts.bak +342 -0
  22. package/dist/types.d.ts.map +1 -0
  23. package/dist/types.js +39 -0
  24. package/dist/types.js.bak +39 -0
  25. package/dist/types.js.map +1 -0
  26. package/dist/utils/data-processor.d.ts +64 -0
  27. package/dist/utils/data-processor.d.ts.bak +64 -0
  28. package/dist/utils/data-processor.d.ts.map +1 -0
  29. package/dist/utils/data-processor.js +326 -0
  30. package/dist/utils/data-processor.js.bak +326 -0
  31. package/dist/utils/data-processor.js.map +1 -0
  32. package/package.json +41 -0
  33. package/src/charts/canvas-renderer.ts +735 -0
  34. package/src/charts/canvas-renderer.ts.bak +735 -0
  35. package/src/charts/svg-renderer.test.ts.bak +837 -0
  36. package/src/charts/svg-renderer.ts +380 -0
  37. package/src/charts/svg-renderer.ts.bak +380 -0
  38. package/src/index.ts +64 -0
  39. package/src/index.ts.bak +64 -0
  40. package/src/types.ts +410 -0
  41. package/src/types.ts.bak +410 -0
  42. package/src/utils/data-processor.test.ts.bak +720 -0
  43. package/src/utils/data-processor.ts +420 -0
  44. package/src/utils/data-processor.ts.bak +420 -0
  45. package/tsconfig.json +22 -0
@@ -0,0 +1,380 @@
1
+ /**
2
+ * SVG Chart Renderer
3
+ *
4
+ * Renders charts to SVG format.
5
+ */
6
+
7
+ import type { ChartConfig, DataSeries, DataPoint, ChartMetadata, ChartPalette } from '../types';
8
+ import { palettes } from '../types';
9
+
10
+ export interface SVGRenderer {
11
+ /** Render chart to SVG string */
12
+ render(config: ChartConfig, series: DataSeries[]): string;
13
+ /** Get chart metadata */
14
+ getMetadata(type: string): ChartMetadata;
15
+ }
16
+
17
+ /**
18
+ * Create SVG chart renderer
19
+ */
20
+ export function createSVGRenderer(): SVGRenderer {
21
+ const defaultWidth = 800;
22
+ const defaultHeight = 400;
23
+ const margin = { top: 60, right: 40, bottom: 60, left: 80 };
24
+
25
+ /**
26
+ * Render chart to SVG
27
+ */
28
+ function render(config: ChartConfig, series: DataSeries[]): string {
29
+ const width = config.width || defaultWidth;
30
+ const height = config.height || defaultHeight;
31
+ const palette = palettes[config.theme || 'default'];
32
+
33
+ const chartWidth = width - margin.left - margin.right;
34
+ const chartHeight = height - margin.top - margin.bottom;
35
+
36
+ let svg = `<svg width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" xmlns="http://www.w3.org/2000/svg">\n`;
37
+
38
+ // Background
39
+ svg += ` <rect width="${width}" height="${height}" fill="${palette.background[0]}" />\n`;
40
+
41
+ // Title
42
+ if (config.title) {
43
+ svg += ` <text x="${width / 2}" y="30" text-anchor="middle" font-family="sans-serif" font-size="18" font-weight="bold" fill="${palette.text.primary}">${escapeHtml(config.title)}</text>\n`;
44
+ }
45
+
46
+ // Chart group
47
+ svg += ` <g transform="translate(${margin.left}, ${margin.top})">\n`;
48
+
49
+ // Render based on chart type
50
+ switch (config.type) {
51
+ case 'bar':
52
+ svg += renderBarChart(chartWidth, chartHeight, series, palette, config);
53
+ break;
54
+ case 'line':
55
+ svg += renderLineChart(chartWidth, chartHeight, series, palette, config);
56
+ break;
57
+ case 'pie':
58
+ case 'donut':
59
+ svg += renderPieChart(chartWidth, chartHeight, series, palette, config);
60
+ break;
61
+ case 'area':
62
+ svg += renderAreaChart(chartWidth, chartHeight, series, palette, config);
63
+ break;
64
+ default:
65
+ svg += renderPlaceholder(chartWidth, chartHeight, config.type);
66
+ }
67
+
68
+ // Axes
69
+ if (['bar', 'line', 'area'].includes(config.type)) {
70
+ svg += renderAxes(chartWidth, chartHeight, series, config, palette);
71
+ }
72
+
73
+ svg += ` </g>\n`;
74
+ svg += `</svg>`;
75
+
76
+ return svg;
77
+ }
78
+
79
+ /**
80
+ * Render bar chart
81
+ */
82
+ function renderBarChart(
83
+ width: number,
84
+ height: number,
85
+ series: DataSeries[],
86
+ palette: ChartPalette,
87
+ config: ChartConfig
88
+ ): string {
89
+ let svg = '';
90
+
91
+ if (series.length === 0) return svg;
92
+
93
+ const allData = series.flatMap(s => s.data);
94
+ const maxValue = Math.max(...allData.map(d => (d.y || d.value || 0) as number));
95
+ const categories = [...new Set(allData.map(d => d.x || d.name))];
96
+
97
+ const barWidth = (width / categories.length) * 0.6 / series.length;
98
+ const scaleY = height / (maxValue * 1.1);
99
+
100
+ series.forEach((s, seriesIndex) => {
101
+ const color = s.color || palette.primary[seriesIndex % palette.primary.length];
102
+
103
+ s.data.forEach((point, pointIndex) => {
104
+ const value = (point.y || point.value || 0) as number;
105
+ const barHeight = value * scaleY;
106
+ const x = (pointIndex + 0.2) * (width / categories.length) + seriesIndex * barWidth;
107
+ const y = height - barHeight;
108
+
109
+ svg += ` <rect x="${x}" y="${y}" width="${barWidth}" height="${barHeight}" fill="${color}" />\n`;
110
+ });
111
+ });
112
+
113
+ return svg;
114
+ }
115
+
116
+ /**
117
+ * Render line chart
118
+ */
119
+ function renderLineChart(
120
+ width: number,
121
+ height: number,
122
+ series: DataSeries[],
123
+ palette: ChartPalette,
124
+ config: ChartConfig
125
+ ): string {
126
+ let svg = '';
127
+
128
+ if (series.length === 0) return svg;
129
+
130
+ const allData = series.flatMap(s => s.data);
131
+ const maxValue = Math.max(...allData.map(d => (d.y || 0) as number));
132
+ const scaleX = width / (allData.length - 1 || 1);
133
+ const scaleY = height / (maxValue * 1.1);
134
+
135
+ series.forEach((s, seriesIndex) => {
136
+ const color = s.color || palette.primary[seriesIndex % palette.primary.length];
137
+
138
+ // Generate path
139
+ let path = '';
140
+ s.data.forEach((point, index) => {
141
+ const x = index * scaleX;
142
+ const y = height - ((point.y || 0) as number) * scaleY;
143
+ path += index === 0 ? `M ${x} ${y}` : ` L ${x} ${y}`;
144
+ });
145
+
146
+ svg += ` <path d="${path}" fill="none" stroke="${color}" stroke-width="2" />\n`;
147
+
148
+ // Data points
149
+ if (config.dataLabels) {
150
+ s.data.forEach((point, index) => {
151
+ const x = index * scaleX;
152
+ const y = height - ((point.y || 0) as number) * scaleY;
153
+ svg += ` <circle cx="${x}" cy="${y}" r="4" fill="${color}" />\n`;
154
+ });
155
+ }
156
+ });
157
+
158
+ return svg;
159
+ }
160
+
161
+ /**
162
+ * Render pie/donut chart
163
+ */
164
+ function renderPieChart(
165
+ width: number,
166
+ height: number,
167
+ series: DataSeries[],
168
+ palette: ChartPalette,
169
+ config: ChartConfig
170
+ ): string {
171
+ let svg = '';
172
+
173
+ if (series.length === 0) return svg;
174
+
175
+ const data = series[0].data;
176
+ const total = data.reduce((sum, d) => sum + ((d.y || d.value || 0) as number), 0);
177
+
178
+ const centerX = width / 2;
179
+ const centerY = height / 2;
180
+ const radius = Math.min(width, height) / 2 * 0.8;
181
+ const innerRadius = config.type === 'donut' ? radius * 0.5 : 0;
182
+
183
+ let currentAngle = -Math.PI / 2;
184
+
185
+ data.forEach((point, index) => {
186
+ const value = (point.y || point.value || 0) as number;
187
+ const angle = (value / total) * 2 * Math.PI;
188
+ const color = palette.primary[index % palette.primary.length];
189
+
190
+ const x1 = centerX + Math.cos(currentAngle) * radius;
191
+ const y1 = centerY + Math.sin(currentAngle) * radius;
192
+ const x2 = centerX + Math.cos(currentAngle + angle) * radius;
193
+ const y2 = centerY + Math.sin(currentAngle + angle) * radius;
194
+
195
+ const largeArc = angle > Math.PI ? 1 : 0;
196
+
197
+ if (innerRadius > 0) {
198
+ // Donut
199
+ const ix1 = centerX + Math.cos(currentAngle) * innerRadius;
200
+ const iy1 = centerY + Math.sin(currentAngle) * innerRadius;
201
+ const ix2 = centerX + Math.cos(currentAngle + angle) * innerRadius;
202
+ const iy2 = centerY + Math.sin(currentAngle + angle) * innerRadius;
203
+
204
+ svg += ` <path d="M ${x1} ${y1} A ${radius} ${radius} 0 ${largeArc} 1 ${x2} ${y2} L ${ix2} ${iy2} A ${innerRadius} ${innerRadius} 0 ${largeArc} 0 ${ix1} ${iy1} Z" fill="${color}" />\n`;
205
+ } else {
206
+ // Pie
207
+ svg += ` <path d="M ${centerX} ${centerY} L ${x1} ${y1} A ${radius} ${radius} 0 ${largeArc} 1 ${x2} ${y2} Z" fill="${color}" />\n`;
208
+ }
209
+
210
+ currentAngle += angle;
211
+ });
212
+
213
+ return svg;
214
+ }
215
+
216
+ /**
217
+ * Render area chart
218
+ */
219
+ function renderAreaChart(
220
+ width: number,
221
+ height: number,
222
+ series: DataSeries[],
223
+ palette: ChartPalette,
224
+ config: ChartConfig
225
+ ): string {
226
+ let svg = '';
227
+
228
+ if (series.length === 0) return svg;
229
+
230
+ const allData = series.flatMap(s => s.data);
231
+ const maxValue = Math.max(...allData.map(d => (d.y || 0) as number));
232
+ const scaleX = width / (allData.length - 1 || 1);
233
+ const scaleY = height / (maxValue * 1.1);
234
+
235
+ series.forEach((s, seriesIndex) => {
236
+ const color = s.color || palette.primary[seriesIndex % palette.primary.length];
237
+ const fillColor = color + '40'; // 25% opacity
238
+
239
+ // Generate area path
240
+ let path = '';
241
+ s.data.forEach((point, index) => {
242
+ const x = index * scaleX;
243
+ const y = height - ((point.y || 0) as number) * scaleY;
244
+ path += index === 0 ? `M ${x} ${y}` : ` L ${x} ${y}`;
245
+ });
246
+
247
+ // Close the area
248
+ path += ` L ${width} ${height} L 0 ${height} Z`;
249
+
250
+ svg += ` <path d="${path}" fill="${fillColor}" stroke="${color}" stroke-width="2" />\n`;
251
+ });
252
+
253
+ return svg;
254
+ }
255
+
256
+ /**
257
+ * Render placeholder for unsupported chart types
258
+ */
259
+ function renderPlaceholder(width: number, height: number, type: string): string {
260
+ return ` <rect x="${width / 4}" y="${height / 3}" width="${width / 2}" height="${height / 3}" fill="#e5e7eb" rx="8" />\n <text x="${width / 2}" y="${height / 2}" text-anchor="middle" font-family="sans-serif" font-size="14" fill="#6b7280">${type} chart</text>\n`;
261
+ }
262
+
263
+ /**
264
+ * Render axes
265
+ */
266
+ function renderAxes(
267
+ width: number,
268
+ height: number,
269
+ series: DataSeries[],
270
+ config: ChartConfig,
271
+ palette: ChartPalette
272
+ ): string {
273
+ let svg = '';
274
+
275
+ // Y-axis
276
+ svg += ` <line x1="0" y1="0" x2="0" y2="${height}" stroke="${palette.background[2]}" stroke-width="1" />\n`;
277
+
278
+ // X-axis
279
+ svg += ` <line x1="0" y1="${height}" x2="${width}" y2="${height}" stroke="${palette.background[2]}" stroke-width="1" />\n`;
280
+
281
+ // Y-axis labels
282
+ const yAxis = config.yAxis || {};
283
+ const maxValue = Math.max(...series.flatMap(s => s.data.map(d => (d.y || 0) as number)));
284
+ const steps = 5;
285
+
286
+ for (let i = 0; i <= steps; i++) {
287
+ const value = (maxValue / steps) * i;
288
+ const y = height - (height / steps) * i;
289
+ svg += ` <text x="-10" y="${y + 4}" text-anchor="end" font-family="sans-serif" font-size="12" fill="${palette.text.secondary}">${Math.round(value)}</text>\n`;
290
+
291
+ // Grid line
292
+ if (config.xAxis?.grid?.enabled !== false && i > 0) {
293
+ svg += ` <line x1="0" y1="${y}" x2="${width}" y2="${y}" stroke="${palette.background[2]}" stroke-width="1" stroke-dasharray="4,4" />\n`;
294
+ }
295
+ }
296
+
297
+ // Axis titles
298
+ if (yAxis.title) {
299
+ svg += ` <text x="${-margin.left + 20}" y="${height / 2}" transform="rotate(-90, ${-margin.left + 20}, ${height / 2})" text-anchor="middle" font-family="sans-serif" font-size="12" fill="${palette.text.secondary}">${escapeHtml(yAxis.title)}</text>\n`;
300
+ }
301
+
302
+ if (config.xAxis?.title) {
303
+ svg += ` <text x="${width / 2}" y="${height + 40}" text-anchor="middle" font-family="sans-serif" font-size="12" fill="${palette.text.secondary}">${escapeHtml(config.xAxis.title)}</text>\n`;
304
+ }
305
+
306
+ return svg;
307
+ }
308
+
309
+ /**
310
+ * Escape HTML special characters
311
+ */
312
+ function escapeHtml(text: string): string {
313
+ return text
314
+ .replace(/&/g, '&amp;')
315
+ .replace(/</g, '&lt;')
316
+ .replace(/>/g, '&gt;')
317
+ .replace(/"/g, '&quot;')
318
+ .replace(/'/g, '&#039;');
319
+ }
320
+
321
+ /**
322
+ * Get chart metadata
323
+ */
324
+ function getMetadata(type: string): ChartMetadata {
325
+ const metadata: Record<string, ChartMetadata> = {
326
+ line: {
327
+ type: 'line',
328
+ name: 'Line Chart',
329
+ description: 'Show trends over time or categories',
330
+ axes: ['x', 'y'],
331
+ dataStructure: 'Array of { x, y } points',
332
+ useCases: ['Time series', 'Trends', 'Progress over time'],
333
+ },
334
+ bar: {
335
+ type: 'bar',
336
+ name: 'Bar Chart',
337
+ description: 'Compare values across categories',
338
+ axes: ['x', 'y'],
339
+ dataStructure: 'Array of { x, y } points',
340
+ useCases: ['Comparisons', 'Rankings', 'Category distribution'],
341
+ },
342
+ pie: {
343
+ type: 'pie',
344
+ name: 'Pie Chart',
345
+ description: 'Show part-to-whole relationships',
346
+ axes: [],
347
+ dataStructure: 'Array of { name, value }',
348
+ useCases: ['Composition', 'Percentage distribution'],
349
+ },
350
+ donut: {
351
+ type: 'donut',
352
+ name: 'Donut Chart',
353
+ description: 'Show part-to-whole with center space',
354
+ axes: [],
355
+ dataStructure: 'Array of { name, value }',
356
+ useCases: ['Composition', 'Percentage distribution'],
357
+ },
358
+ area: {
359
+ type: 'area',
360
+ name: 'Area Chart',
361
+ description: 'Show cumulative totals over time',
362
+ axes: ['x', 'y'],
363
+ dataStructure: 'Array of { x, y } points',
364
+ useCases: ['Volume over time', 'Cumulative data'],
365
+ },
366
+ };
367
+
368
+ return metadata[type] || metadata.line;
369
+ }
370
+
371
+ return {
372
+ render,
373
+ getMetadata,
374
+ };
375
+ }
376
+
377
+ /**
378
+ * Global SVG renderer instance
379
+ */
380
+ export const globalSVGRenderer = createSVGRenderer();
package/src/index.ts ADDED
@@ -0,0 +1,64 @@
1
+ /**
2
+ * A2R Data Visualization
3
+ *
4
+ * Charts, graphs, and dashboards for A2R platform.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { createSVGRenderer, createDashboard } from '@allternit/viz';
9
+ *
10
+ * const renderer = createSVGRenderer();
11
+ *
12
+ * const svg = renderer.render({
13
+ * type: 'bar',
14
+ * title: 'Sales by Month',
15
+ * width: 800,
16
+ * height: 400,
17
+ * }, [
18
+ * {
19
+ * id: 'sales',
20
+ * name: 'Sales',
21
+ * data: [
22
+ * { x: 'Jan', y: 100 },
23
+ * { x: 'Feb', y: 150 },
24
+ * { x: 'Mar', y: 200 },
25
+ * ],
26
+ * },
27
+ * ]);
28
+ * ```
29
+ */
30
+
31
+ // Types
32
+ export type {
33
+ ChartType,
34
+ ChartConfig,
35
+ AxisConfig,
36
+ LegendConfig,
37
+ TooltipConfig,
38
+ DataSeries,
39
+ DataPoint,
40
+ DashboardConfig,
41
+ WidgetConfig,
42
+ GridPosition,
43
+ MetricConfig,
44
+ TableConfig,
45
+ TableColumn,
46
+ DataSource,
47
+ DataTransform,
48
+ ChartMetadata,
49
+ ChartPalette,
50
+ ExportOptions,
51
+ VizEvent,
52
+ } from './types';
53
+
54
+ export { palettes } from './types';
55
+
56
+ // Charts
57
+ export {
58
+ createSVGRenderer,
59
+ globalSVGRenderer,
60
+ type SVGRenderer,
61
+ } from './charts/svg-renderer';
62
+
63
+ // Version
64
+ export const VERSION = '0.1.0';
@@ -0,0 +1,64 @@
1
+ /**
2
+ * A2R Data Visualization
3
+ *
4
+ * Charts, graphs, and dashboards for A2R platform.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { createSVGRenderer, createDashboard } from '@allternit/viz';
9
+ *
10
+ * const renderer = createSVGRenderer();
11
+ *
12
+ * const svg = renderer.render({
13
+ * type: 'bar',
14
+ * title: 'Sales by Month',
15
+ * width: 800,
16
+ * height: 400,
17
+ * }, [
18
+ * {
19
+ * id: 'sales',
20
+ * name: 'Sales',
21
+ * data: [
22
+ * { x: 'Jan', y: 100 },
23
+ * { x: 'Feb', y: 150 },
24
+ * { x: 'Mar', y: 200 },
25
+ * ],
26
+ * },
27
+ * ]);
28
+ * ```
29
+ */
30
+
31
+ // Types
32
+ export type {
33
+ ChartType,
34
+ ChartConfig,
35
+ AxisConfig,
36
+ LegendConfig,
37
+ TooltipConfig,
38
+ DataSeries,
39
+ DataPoint,
40
+ DashboardConfig,
41
+ WidgetConfig,
42
+ GridPosition,
43
+ MetricConfig,
44
+ TableConfig,
45
+ TableColumn,
46
+ DataSource,
47
+ DataTransform,
48
+ ChartMetadata,
49
+ ChartPalette,
50
+ ExportOptions,
51
+ VizEvent,
52
+ } from './types';
53
+
54
+ export { palettes } from './types';
55
+
56
+ // Charts
57
+ export {
58
+ createSVGRenderer,
59
+ globalSVGRenderer,
60
+ type SVGRenderer,
61
+ } from './charts/svg-renderer';
62
+
63
+ // Version
64
+ export const VERSION = '0.1.0';