juxscript 1.0.19 → 1.0.21

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 (77) hide show
  1. package/bin/cli.js +121 -72
  2. package/lib/components/alert.ts +212 -165
  3. package/lib/components/badge.ts +93 -103
  4. package/lib/components/base/BaseComponent.ts +397 -0
  5. package/lib/components/base/FormInput.ts +322 -0
  6. package/lib/components/button.ts +63 -122
  7. package/lib/components/card.ts +109 -155
  8. package/lib/components/charts/areachart.ts +315 -0
  9. package/lib/components/charts/barchart.ts +421 -0
  10. package/lib/components/charts/doughnutchart.ts +263 -0
  11. package/lib/components/charts/lib/BaseChart.ts +402 -0
  12. package/lib/components/charts/lib/chart-types.ts +159 -0
  13. package/lib/components/charts/lib/chart-utils.ts +160 -0
  14. package/lib/components/charts/lib/chart.ts +707 -0
  15. package/lib/components/checkbox.ts +264 -127
  16. package/lib/components/code.ts +75 -108
  17. package/lib/components/container.ts +113 -130
  18. package/lib/components/data.ts +37 -5
  19. package/lib/components/datepicker.ts +195 -147
  20. package/lib/components/dialog.ts +187 -157
  21. package/lib/components/divider.ts +85 -191
  22. package/lib/components/docs-data.json +544 -2027
  23. package/lib/components/dropdown.ts +178 -136
  24. package/lib/components/element.ts +227 -171
  25. package/lib/components/fileupload.ts +285 -228
  26. package/lib/components/guard.ts +92 -0
  27. package/lib/components/heading.ts +46 -69
  28. package/lib/components/helpers.ts +13 -6
  29. package/lib/components/hero.ts +107 -95
  30. package/lib/components/icon.ts +160 -0
  31. package/lib/components/icons.ts +175 -0
  32. package/lib/components/include.ts +153 -5
  33. package/lib/components/input.ts +174 -374
  34. package/lib/components/kpicard.ts +16 -16
  35. package/lib/components/list.ts +378 -240
  36. package/lib/components/loading.ts +142 -211
  37. package/lib/components/menu.ts +103 -97
  38. package/lib/components/modal.ts +138 -144
  39. package/lib/components/nav.ts +169 -90
  40. package/lib/components/paragraph.ts +49 -150
  41. package/lib/components/progress.ts +118 -200
  42. package/lib/components/radio.ts +297 -149
  43. package/lib/components/script.ts +19 -87
  44. package/lib/components/select.ts +184 -186
  45. package/lib/components/sidebar.ts +152 -140
  46. package/lib/components/style.ts +19 -82
  47. package/lib/components/switch.ts +258 -188
  48. package/lib/components/table.ts +1117 -170
  49. package/lib/components/tabs.ts +162 -145
  50. package/lib/components/theme-toggle.ts +108 -169
  51. package/lib/components/tooltip.ts +86 -157
  52. package/lib/components/write.ts +108 -127
  53. package/lib/jux.ts +86 -41
  54. package/machinery/build.js +466 -0
  55. package/machinery/compiler.js +354 -105
  56. package/machinery/server.js +23 -100
  57. package/machinery/watcher.js +153 -130
  58. package/package.json +1 -2
  59. package/presets/base.css +1166 -0
  60. package/presets/notion.css +2 -1975
  61. package/lib/adapters/base-adapter.js +0 -35
  62. package/lib/adapters/index.js +0 -33
  63. package/lib/adapters/mysql-adapter.js +0 -65
  64. package/lib/adapters/postgres-adapter.js +0 -70
  65. package/lib/adapters/sqlite-adapter.js +0 -56
  66. package/lib/components/areachart.ts +0 -1246
  67. package/lib/components/areachartsmooth.ts +0 -1380
  68. package/lib/components/barchart.ts +0 -1250
  69. package/lib/components/chart.ts +0 -127
  70. package/lib/components/doughnutchart.ts +0 -1191
  71. package/lib/components/footer.ts +0 -165
  72. package/lib/components/header.ts +0 -187
  73. package/lib/components/layout.ts +0 -239
  74. package/lib/components/main.ts +0 -137
  75. package/lib/layouts/default.jux +0 -8
  76. package/lib/layouts/figma.jux +0 -0
  77. /package/lib/{themes → components/charts/lib}/charts.js +0 -0
@@ -1,1380 +0,0 @@
1
- import { getOrCreateContainer } from './helpers.js';
2
- import { State } from '../reactivity/state.js';
3
- import {
4
- googleTheme,
5
- seriesaTheme,
6
- hrTheme,
7
- figmaTheme,
8
- notionTheme,
9
- chalkTheme,
10
- mintTheme
11
- } from '../themes/charts.js';
12
-
13
- /**
14
- * Bar chart data point
15
- */
16
- export interface AreaChartSmoothDataPoint {
17
- label: string;
18
- value: number;
19
- color?: string;
20
- }
21
-
22
- /**
23
- * Bar chart options
24
- */
25
- export interface AreaChartSmoothOptions {
26
- data?: AreaChartSmoothDataPoint[];
27
- title?: string;
28
- subtitle?: string;
29
- xAxisLabel?: string;
30
- yAxisLabel?: string;
31
- showTicksX?: boolean;
32
- showTicksY?: boolean;
33
- showScaleX?: boolean;
34
- showScaleY?: boolean;
35
- scaleXUnit?: string;
36
- scaleYUnit?: string;
37
- showLegend?: boolean;
38
- legendOrientation?: 'horizontal' | 'vertical';
39
- showDataTable?: boolean;
40
- showDataLabels?: boolean;
41
- animate?: boolean;
42
- animationDuration?: number;
43
- chartOrientation?: 'vertical' | 'horizontal'; // NEW
44
- chartDirection?: 'normal' | 'reverse'; // NEW: normal = bottom-to-top or left-to-right, reverse = opposite
45
- width?: number;
46
- height?: number;
47
- colors?: string[];
48
- class?: string;
49
- style?: string;
50
- theme?: 'google' | 'seriesa' | 'hr' | 'figma' | 'notion' | 'chalk' | 'mint';
51
- styleMode?: 'default' | 'gradient' | 'glow' | 'glass';
52
- borderRadius?: number;
53
- }
54
-
55
- /**
56
- * Bar chart state
57
- */
58
- type AreaChartSmoothState = {
59
- data: AreaChartSmoothDataPoint[];
60
- title: string;
61
- subtitle: string;
62
- xAxisLabel: string;
63
- yAxisLabel: string;
64
- showTicksX: boolean;
65
- showTicksY: boolean;
66
- showScaleX: boolean;
67
- showScaleY: boolean;
68
- scaleXUnit: string;
69
- scaleYUnit: string;
70
- showLegend: boolean;
71
- legendOrientation: 'horizontal' | 'vertical';
72
- showDataTable: boolean;
73
- showDataLabels: boolean;
74
- animate: boolean;
75
- animationDuration: number;
76
- chartOrientation: 'vertical' | 'horizontal'; // NEW
77
- chartDirection: 'normal' | 'reverse'; // NEW
78
- width: number;
79
- height: number;
80
- colors: string[];
81
- class: string;
82
- style: string;
83
- theme?: 'google' | 'seriesa' | 'hr' | 'figma' | 'notion' | 'chalk' | 'mint';
84
- styleMode: 'default' | 'gradient' | 'glow' | 'glass';
85
- borderRadius: number;
86
- };
87
-
88
- /**
89
- * Bar chart component - Simple SVG-based bar chart
90
- *
91
- * Usage:
92
- * jux.areachartsmooth('sales-chart')
93
- * .data([
94
- * { label: 'Jan', value: 100 },
95
- * { label: 'Feb', value: 150 },
96
- * { label: 'Mar', value: 200 }
97
- * ])
98
- * .title('Monthly Sales')
99
- * .showLegend(true)
100
- * .render('#app');
101
- */
102
- export class AreaChartSmooth {
103
- state: AreaChartSmoothState;
104
- container: HTMLElement | null = null;
105
- _id: string;
106
- id: string;
107
-
108
- // State bindings
109
- private _boundTheme?: State<string>;
110
- private _boundStyleMode?: State<string>;
111
- private _boundBorderRadius?: State<number>;
112
-
113
- constructor(id: string, options: AreaChartSmoothOptions = {}) {
114
- this._id = id;
115
- this.id = id;
116
-
117
- let defaultColors = [
118
- '#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6',
119
- '#ec4899', '#06b6d4', '#f97316', '#84cc16', '#6366f1'
120
- ];
121
- // using defaultColors, select a random color from the list.
122
- const randomColor = defaultColors[Math.floor(Math.random() * defaultColors.length)];
123
- defaultColors = [randomColor];
124
-
125
- this.state = {
126
- data: options.data ?? [],
127
- title: options.title ?? '',
128
- subtitle: options.subtitle ?? '',
129
- xAxisLabel: options.xAxisLabel ?? '',
130
- yAxisLabel: options.yAxisLabel ?? '',
131
- showTicksX: options.showTicksX ?? true,
132
- showTicksY: options.showTicksY ?? true,
133
- showScaleX: options.showScaleX ?? true,
134
- showScaleY: options.showScaleY ?? true,
135
- scaleXUnit: options.scaleXUnit ?? '',
136
- scaleYUnit: options.scaleYUnit ?? '',
137
- showLegend: options.showLegend ?? false,
138
- legendOrientation: options.legendOrientation ?? 'horizontal',
139
- showDataTable: options.showDataTable ?? false,
140
- showDataLabels: options.showDataLabels ?? true,
141
- animate: options.animate ?? true,
142
- animationDuration: options.animationDuration ?? 800,
143
- chartOrientation: options.chartOrientation ?? 'vertical', // NEW
144
- chartDirection: options.chartDirection ?? 'normal', // NEW
145
- width: options.width ?? 600,
146
- height: options.height ?? 400,
147
- colors: options.colors ?? defaultColors,
148
- class: options.class ?? '',
149
- style: options.style ?? '',
150
- theme: options.theme,
151
- styleMode: options.styleMode ?? 'default',
152
- borderRadius: options.borderRadius ?? 4
153
- };
154
- }
155
-
156
- /* -------------------------
157
- * State Binding Methods
158
- * ------------------------- */
159
-
160
- /**
161
- * Bind theme to reactive state
162
- */
163
- bindTheme(stateObj: State<string>): this {
164
- this._boundTheme = stateObj;
165
-
166
- stateObj.subscribe((val) => {
167
- this.theme(val as any);
168
- });
169
-
170
- return this;
171
- }
172
-
173
- /**
174
- * Bind styleMode to reactive state
175
- */
176
- bindStyleMode(stateObj: State<string>): this {
177
- this._boundStyleMode = stateObj;
178
-
179
- stateObj.subscribe((val) => {
180
- this.styleMode(val as any);
181
- });
182
-
183
- return this;
184
- }
185
-
186
- /**
187
- * Bind borderRadius to reactive state
188
- */
189
- bindBorderRadius(stateObj: State<number>): this {
190
- this._boundBorderRadius = stateObj;
191
-
192
- stateObj.subscribe((val) => {
193
- this.borderRadius(val);
194
- });
195
-
196
- return this;
197
- }
198
-
199
- /* -------------------------
200
- * Fluent API
201
- * ------------------------- */
202
-
203
- data(value: AreaChartSmoothDataPoint[]): this {
204
- this.state.data = value;
205
- this._updateChart();
206
- return this;
207
- }
208
-
209
- title(value: string): this {
210
- this.state.title = value;
211
- this._updateChart();
212
- return this;
213
- }
214
-
215
- subtitle(value: string): this {
216
- this.state.subtitle = value;
217
- this._updateChart();
218
- return this;
219
- }
220
-
221
- xAxisLabel(value: string): this {
222
- this.state.xAxisLabel = value;
223
- this._updateChart();
224
- return this;
225
- }
226
-
227
- yAxisLabel(value: string): this {
228
- this.state.yAxisLabel = value;
229
- this._updateChart();
230
- return this;
231
- }
232
-
233
- showTicksX(value: boolean): this {
234
- this.state.showTicksX = value;
235
- this._updateChart();
236
- return this;
237
- }
238
-
239
- showTicksY(value: boolean): this {
240
- this.state.showTicksY = value;
241
- this._updateChart();
242
- return this;
243
- }
244
-
245
- showScaleX(value: boolean): this {
246
- this.state.showScaleX = value;
247
- this._updateChart();
248
- return this;
249
- }
250
-
251
- showScaleY(value: boolean): this {
252
- this.state.showScaleY = value;
253
- this._updateChart();
254
- return this;
255
- }
256
-
257
- scaleXUnit(value: string): this {
258
- this.state.scaleXUnit = value;
259
- this._updateChart();
260
- return this;
261
- }
262
-
263
- scaleYUnit(value: string): this {
264
- this.state.scaleYUnit = value;
265
- this._updateChart();
266
- return this;
267
- }
268
-
269
- showLegend(value: boolean): this {
270
- this.state.showLegend = value;
271
- this._updateChart();
272
- return this;
273
- }
274
-
275
- legendOrientation(value: 'horizontal' | 'vertical'): this {
276
- this.state.legendOrientation = value;
277
- this._updateChart();
278
- return this;
279
- }
280
-
281
- showDataTable(value: boolean): this {
282
- this.state.showDataTable = value;
283
- this._updateChart();
284
- return this;
285
- }
286
-
287
- /**
288
- * Show/hide value labels on bars
289
- */
290
- showDataLabels(value: boolean): this {
291
- this.state.showDataLabels = value;
292
- this._updateChart();
293
- return this;
294
- }
295
-
296
- /**
297
- * Enable/disable bar grow animation
298
- */
299
- animate(value: boolean): this {
300
- this.state.animate = value;
301
- this._updateChart();
302
- return this;
303
- }
304
-
305
- /**
306
- * Set animation duration in milliseconds
307
- */
308
- animationDuration(value: number): this {
309
- this.state.animationDuration = value;
310
- this._updateChart();
311
- return this;
312
- }
313
-
314
- /**
315
- * Set chart orientation (vertical bars or horizontal bars)
316
- */
317
- chartOrientation(value: 'vertical' | 'horizontal'): this {
318
- this.state.chartOrientation = value;
319
- this._updateChart();
320
- return this;
321
- }
322
-
323
- /**
324
- * Set chart direction (normal or reverse)
325
- * For vertical: normal = bottom-to-top, reverse = top-to-bottom
326
- * For horizontal: normal = left-to-right, reverse = right-to-left
327
- */
328
- chartDirection(value: 'normal' | 'reverse'): this {
329
- this.state.chartDirection = value;
330
- this._updateChart();
331
- return this;
332
- }
333
-
334
- width(value: number): this {
335
- this.state.width = value;
336
- this._updateChart();
337
- return this;
338
- }
339
-
340
- height(value: number): this {
341
- this.state.height = value;
342
- this._updateChart();
343
- return this;
344
- }
345
-
346
- colors(value: string[]): this {
347
- this.state.colors = value;
348
- this._updateChart();
349
- return this;
350
- }
351
-
352
- class(value: string): this {
353
- this.state.class = value;
354
- return this;
355
- }
356
-
357
- style(value: string): this {
358
- this.state.style = value;
359
- return this;
360
- }
361
-
362
- /**
363
- * Set chart theme
364
- */
365
- theme(value: 'google' | 'seriesa' | 'hr' | 'figma' | 'notion' | 'chalk' | 'mint'): this {
366
- this.state.theme = value;
367
- this._applyTheme(value);
368
- this._updateChart();
369
- return this;
370
- }
371
-
372
- /**
373
- * Set bar style mode
374
- */
375
- styleMode(value: 'default' | 'gradient' | 'glow' | 'glass'): this {
376
- this.state.styleMode = value;
377
- this._updateChart();
378
- return this;
379
- }
380
-
381
- /**
382
- * Set border radius for bars (0 = sharp corners, higher = rounder)
383
- */
384
- borderRadius(value: number): this {
385
- this.state.borderRadius = value;
386
- this._updateChart();
387
- return this;
388
- }
389
-
390
- /* -------------------------
391
- * Update chart
392
- * ------------------------- */
393
-
394
- private _updateChart(): void {
395
- if (!this.container) return;
396
-
397
- // Find the wrapper div
398
- const wrapper = this.container.querySelector(`#${this._id}`) as HTMLElement;
399
- if (!wrapper) return;
400
-
401
- // Clear and rebuild
402
- wrapper.innerHTML = '';
403
- this._buildChart(wrapper);
404
-
405
- // Reapply theme after rebuild
406
- if (this.state.theme) {
407
- this._applyThemeToWrapper(wrapper);
408
- }
409
- }
410
-
411
- private _buildChart(wrapper: HTMLElement): void {
412
- const { data, title, subtitle, width, height, showLegend, showDataTable } = this.state;
413
-
414
- // Title
415
- if (title) {
416
- const titleEl = document.createElement('h3');
417
- titleEl.className = 'jux-areachartsmooth-title';
418
- titleEl.textContent = title;
419
- wrapper.appendChild(titleEl);
420
- }
421
-
422
- // Subtitle
423
- if (subtitle) {
424
- const subtitleEl = document.createElement('p');
425
- subtitleEl.className = 'jux-areachartsmooth-subtitle';
426
- subtitleEl.textContent = subtitle;
427
- wrapper.appendChild(subtitleEl);
428
- }
429
-
430
- // SVG Chart
431
- const svg = this._createSVG();
432
- wrapper.appendChild(svg);
433
-
434
- // Legend
435
- if (showLegend) {
436
- const legend = this._createLegend();
437
- wrapper.appendChild(legend);
438
- }
439
-
440
- // Data Table
441
- if (showDataTable) {
442
- const table = this._createDataTable();
443
- wrapper.appendChild(table);
444
- }
445
- }
446
-
447
- private _createSVG(): SVGSVGElement {
448
- const {
449
- data, width, height, colors, xAxisLabel, yAxisLabel,
450
- showTicksX, showTicksY, showScaleX, showScaleY, scaleYUnit,
451
- styleMode, borderRadius, showDataLabels, animate, animationDuration,
452
- chartOrientation, chartDirection
453
- } = this.state;
454
-
455
- if (!data.length) {
456
- const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
457
- svg.setAttribute('width', width.toString());
458
- svg.setAttribute('height', height.toString());
459
- svg.setAttribute('class', 'jux-areachartsmooth-svg');
460
- return svg;
461
- }
462
-
463
- const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
464
- svg.setAttribute('width', width.toString());
465
- svg.setAttribute('height', height.toString());
466
- svg.setAttribute('class', 'jux-areachartsmooth-svg');
467
-
468
- // Add animation styles to SVG
469
- if (animate) {
470
- const animationId = `bar-grow-${this._id}`;
471
- const style = document.createElementNS('http://www.w3.org/2000/svg', 'style');
472
-
473
- // Different animation based on orientation and direction
474
- let transformOrigin = 'bottom';
475
- let scaleAxis = 'scaleY';
476
-
477
- if (chartOrientation === 'horizontal') {
478
- if (chartDirection === 'normal') {
479
- transformOrigin = 'left';
480
- scaleAxis = 'scaleX';
481
- } else {
482
- transformOrigin = 'right';
483
- scaleAxis = 'scaleX';
484
- }
485
- } else {
486
- if (chartDirection === 'reverse') {
487
- transformOrigin = 'top';
488
- scaleAxis = 'scaleY';
489
- }
490
- }
491
-
492
- style.textContent = `
493
- @keyframes ${animationId} {
494
- from {
495
- transform: ${scaleAxis}(0);
496
- opacity: 0;
497
- }
498
- to {
499
- transform: ${scaleAxis}(1);
500
- opacity: 1;
501
- }
502
- }
503
- .jux-bar-animated {
504
- transform-origin: ${transformOrigin};
505
- animation: ${animationId} ${animationDuration}ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
506
- }
507
- .jux-label-animated {
508
- opacity: 0;
509
- animation: fadeIn ${animationDuration}ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
510
- }
511
- @keyframes fadeIn {
512
- from { opacity: 0; }
513
- to { opacity: 1; }
514
- }
515
- `;
516
- svg.appendChild(style);
517
- }
518
-
519
- // Calculate dimensions
520
- const padding = { top: 40, right: 40, bottom: 60, left: 60 };
521
- const chartWidth = width - padding.left - padding.right;
522
- const chartHeight = height - padding.top - padding.bottom;
523
-
524
- const maxValue = Math.max(...data.map(d => d.value));
525
-
526
- // Determine if we're working with vertical or horizontal layout
527
- const isVertical = chartOrientation === 'vertical';
528
- const isReverse = chartDirection === 'reverse';
529
-
530
- if (isVertical) {
531
- // VERTICAL BARS (original logic with direction support)
532
- this._renderVerticalBars(svg, data, colors, padding, chartWidth, chartHeight, maxValue, isReverse);
533
- } else {
534
- // HORIZONTAL BARS (new logic)
535
- this._renderHorizontalBars(svg, data, colors, padding, chartWidth, chartHeight, maxValue, isReverse);
536
- }
537
-
538
- return svg;
539
- }
540
-
541
- private _renderVerticalBars(
542
- svg: SVGSVGElement,
543
- data: AreaChartSmoothDataPoint[],
544
- colors: string[],
545
- padding: { top: number, right: number, bottom: number, left: number },
546
- chartWidth: number,
547
- chartHeight: number,
548
- maxValue: number,
549
- isReverse: boolean
550
- ): void {
551
- const { width, height, xAxisLabel, yAxisLabel, showTicksX, showTicksY, showScaleX, showScaleY, scaleYUnit, styleMode, showDataLabels, animate, animationDuration } = this.state;
552
-
553
- const yScale = chartHeight / maxValue;
554
- // Change spacing calculation to use data.length - 1 for even distribution
555
- const spacing = chartWidth / (data.length - 1);
556
-
557
- // Y-axis
558
- if (showScaleY) {
559
- const yAxis = document.createElementNS('http://www.w3.org/2000/svg', 'line');
560
- yAxis.setAttribute('x1', padding.left.toString());
561
- yAxis.setAttribute('y1', padding.top.toString());
562
- yAxis.setAttribute('x2', padding.left.toString());
563
- yAxis.setAttribute('y2', (height - padding.bottom).toString());
564
- yAxis.setAttribute('stroke', '#9ca3af');
565
- yAxis.setAttribute('stroke-width', '2');
566
- svg.appendChild(yAxis);
567
-
568
- if (yAxisLabel && showTicksY) {
569
- const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
570
- label.setAttribute('x', '20');
571
- label.setAttribute('y', (padding.top + chartHeight / 2).toString());
572
- label.setAttribute('text-anchor', 'middle');
573
- label.setAttribute('transform', `rotate(-90, 20, ${padding.top + chartHeight / 2})`);
574
- label.setAttribute('fill', '#6b7280');
575
- label.setAttribute('font-size', '12');
576
- label.setAttribute('font-weight', '500');
577
- label.setAttribute('font-family', 'inherit');
578
- label.textContent = yAxisLabel;
579
- svg.appendChild(label);
580
- }
581
-
582
- if (showTicksY) {
583
- const numTicks = 5;
584
- for (let i = 0; i <= numTicks; i++) {
585
- const value = (maxValue / numTicks) * i;
586
- const y = isReverse
587
- ? padding.top + (value * yScale)
588
- : height - padding.bottom - (value * yScale);
589
-
590
- const gridLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
591
- gridLine.setAttribute('x1', padding.left.toString());
592
- gridLine.setAttribute('y1', y.toString());
593
- gridLine.setAttribute('x2', (width - padding.right).toString());
594
- gridLine.setAttribute('y2', y.toString());
595
- gridLine.setAttribute('stroke', '#e5e7eb');
596
- gridLine.setAttribute('stroke-width', '1');
597
- gridLine.setAttribute('stroke-dasharray', '4,4');
598
- svg.appendChild(gridLine);
599
-
600
- const tickLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text');
601
- tickLabel.setAttribute('x', (padding.left - 10).toString());
602
- tickLabel.setAttribute('y', (y + 4).toString());
603
- tickLabel.setAttribute('text-anchor', 'end');
604
- tickLabel.setAttribute('fill', '#6b7280');
605
- tickLabel.setAttribute('font-size', '11');
606
- tickLabel.setAttribute('font-family', 'inherit');
607
- tickLabel.textContent = Math.round(value).toString() + (scaleYUnit || '');
608
- svg.appendChild(tickLabel);
609
- }
610
- }
611
- }
612
-
613
- // X-axis
614
- if (showScaleX) {
615
- const xAxis = document.createElementNS('http://www.w3.org/2000/svg', 'line');
616
- const axisY = isReverse ? padding.top : height - padding.bottom;
617
- xAxis.setAttribute('x1', padding.left.toString());
618
- xAxis.setAttribute('y1', axisY.toString());
619
- xAxis.setAttribute('x2', (width - padding.right).toString());
620
- xAxis.setAttribute('y2', axisY.toString());
621
- xAxis.setAttribute('stroke', '#9ca3af');
622
- xAxis.setAttribute('stroke-width', '2');
623
- svg.appendChild(xAxis);
624
-
625
- if (xAxisLabel && showTicksX) {
626
- const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
627
- label.setAttribute('x', (padding.left + chartWidth / 2).toString());
628
- label.setAttribute('y', (height - 15).toString());
629
- label.setAttribute('text-anchor', 'middle');
630
- label.setAttribute('fill', '#6b7280');
631
- label.setAttribute('font-size', '12');
632
- label.setAttribute('font-weight', '500');
633
- label.setAttribute('font-family', 'inherit');
634
- label.textContent = xAxisLabel;
635
- svg.appendChild(label);
636
- }
637
- }
638
-
639
- // Generate smooth path
640
- const pathData = this._generateSmoothPath(data, padding, chartWidth, chartHeight, maxValue, yScale, spacing, isReverse, false);
641
- const color = colors[0];
642
-
643
- // Create area path with gradient fill
644
- this._renderAreaPath(svg, pathData, color, isReverse ? padding.top : height - padding.bottom, animate);
645
-
646
- // Add data point circles and labels
647
- data.forEach((point, index) => {
648
- // Changed calculation to place points at edges
649
- const x = padding.left + (index * spacing);
650
- const pointY = point.value * yScale;
651
- const y = isReverse ? padding.top + pointY : height - padding.bottom - pointY;
652
-
653
- // Data point circle
654
- const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
655
- circle.setAttribute('cx', x.toString());
656
- circle.setAttribute('cy', y.toString());
657
- circle.setAttribute('r', '4');
658
- circle.setAttribute('fill', 'white');
659
- circle.setAttribute('stroke', color);
660
- circle.setAttribute('stroke-width', '2');
661
-
662
- if (animate) {
663
- circle.classList.add('jux-label-animated');
664
- circle.style.animationDelay = `${animationDuration}ms`;
665
- }
666
-
667
- svg.appendChild(circle);
668
-
669
- // X-axis label
670
- if (showTicksX && showScaleX) {
671
- const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
672
- label.setAttribute('x', x.toString());
673
- label.setAttribute('y', (height - padding.bottom + 20).toString());
674
- label.setAttribute('text-anchor', 'middle');
675
- label.setAttribute('fill', '#6b7280');
676
- label.setAttribute('font-size', '11');
677
- label.setAttribute('font-family', 'inherit');
678
- label.textContent = point.label;
679
-
680
- if (animate) {
681
- label.classList.add('jux-label-animated');
682
- label.style.animationDelay = `${index * 100 + 200}ms`;
683
- }
684
-
685
- svg.appendChild(label);
686
- }
687
-
688
- // Value labels
689
- if (showDataLabels) {
690
- const valueLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text');
691
- valueLabel.setAttribute('x', x.toString());
692
- valueLabel.setAttribute('y', (y - 10).toString());
693
- valueLabel.setAttribute('text-anchor', 'middle');
694
- valueLabel.setAttribute('fill', '#374151');
695
- valueLabel.setAttribute('font-size', '11');
696
- valueLabel.setAttribute('font-weight', '600');
697
- valueLabel.setAttribute('font-family', 'inherit');
698
- valueLabel.textContent = point.value.toString();
699
-
700
- if (animate) {
701
- valueLabel.classList.add('jux-label-animated');
702
- valueLabel.style.animationDelay = `${animationDuration}ms`;
703
- }
704
-
705
- svg.appendChild(valueLabel);
706
- }
707
- });
708
- }
709
-
710
- private _renderHorizontalBars(
711
- svg: SVGSVGElement,
712
- data: AreaChartSmoothDataPoint[],
713
- colors: string[],
714
- padding: { top: number, right: number, bottom: number, left: number },
715
- chartWidth: number,
716
- chartHeight: number,
717
- maxValue: number,
718
- isReverse: boolean
719
- ): void {
720
- const { width, height, xAxisLabel, yAxisLabel, showTicksX, showTicksY, showScaleX, showScaleY, scaleXUnit, showDataLabels, animate, animationDuration } = this.state;
721
-
722
- const xScale = chartWidth / maxValue;
723
- // Change spacing calculation to use data.length - 1 for even distribution
724
- const spacing = chartHeight / (data.length - 1);
725
-
726
- // X-axis (value axis)
727
- if (showScaleX) {
728
- const xAxis = document.createElementNS('http://www.w3.org/2000/svg', 'line');
729
- const axisX = isReverse ? width - padding.right : padding.left;
730
- xAxis.setAttribute('x1', axisX.toString());
731
- xAxis.setAttribute('y1', padding.top.toString());
732
- xAxis.setAttribute('x2', axisX.toString());
733
- xAxis.setAttribute('y2', (height - padding.bottom).toString());
734
- xAxis.setAttribute('stroke', '#9ca3af');
735
- xAxis.setAttribute('stroke-width', '2');
736
- svg.appendChild(xAxis);
737
-
738
- if (xAxisLabel && showTicksX) {
739
- const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
740
- label.setAttribute('x', (padding.left + chartWidth / 2).toString());
741
- label.setAttribute('y', (height - 15).toString());
742
- label.setAttribute('text-anchor', 'middle');
743
- label.setAttribute('fill', '#6b7280');
744
- label.setAttribute('font-size', '12');
745
- label.setAttribute('font-weight', '500');
746
- label.setAttribute('font-family', 'inherit');
747
- label.textContent = xAxisLabel;
748
- svg.appendChild(label);
749
- }
750
-
751
- if (showTicksX) {
752
- const numTicks = 5;
753
- for (let i = 0; i <= numTicks; i++) {
754
- const value = (maxValue / numTicks) * i;
755
- const x = isReverse
756
- ? width - padding.right - (value * xScale)
757
- : padding.left + (value * xScale);
758
-
759
- const gridLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
760
- gridLine.setAttribute('x1', x.toString());
761
- gridLine.setAttribute('y1', padding.top.toString());
762
- gridLine.setAttribute('x2', x.toString());
763
- gridLine.setAttribute('y2', (height - padding.bottom).toString());
764
- gridLine.setAttribute('stroke', '#e5e7eb');
765
- gridLine.setAttribute('stroke-width', '1');
766
- gridLine.setAttribute('stroke-dasharray', '4,4');
767
- svg.appendChild(gridLine);
768
-
769
- const tickLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text');
770
- tickLabel.setAttribute('x', x.toString());
771
- tickLabel.setAttribute('y', (height - padding.bottom + 20).toString());
772
- tickLabel.setAttribute('text-anchor', 'middle');
773
- tickLabel.setAttribute('fill', '#6b7280');
774
- tickLabel.setAttribute('font-size', '11');
775
- tickLabel.setAttribute('font-family', 'inherit');
776
- tickLabel.textContent = Math.round(value).toString() + (scaleXUnit || '');
777
- svg.appendChild(tickLabel);
778
- }
779
- }
780
- }
781
-
782
- // Y-axis (category axis)
783
- if (showScaleY) {
784
- const yAxis = document.createElementNS('http://www.w3.org/2000/svg', 'line');
785
- yAxis.setAttribute('x1', padding.left.toString());
786
- yAxis.setAttribute('y1', padding.top.toString());
787
- yAxis.setAttribute('x2', padding.left.toString());
788
- yAxis.setAttribute('y2', (height - padding.bottom).toString());
789
- yAxis.setAttribute('stroke', '#9ca3af');
790
- yAxis.setAttribute('stroke-width', '2');
791
- svg.appendChild(yAxis);
792
-
793
- if (yAxisLabel && showTicksY) {
794
- const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
795
- label.setAttribute('x', '20');
796
- label.setAttribute('y', (padding.top + chartHeight / 2).toString());
797
- label.setAttribute('text-anchor', 'middle');
798
- label.setAttribute('transform', `rotate(-90, 20, ${padding.top + chartHeight / 2})`);
799
- label.setAttribute('fill', '#6b7280');
800
- label.setAttribute('font-size', '12');
801
- label.setAttribute('font-weight', '500');
802
- label.setAttribute('font-family', 'inherit');
803
- label.textContent = yAxisLabel;
804
- svg.appendChild(label);
805
- }
806
- }
807
-
808
- // Generate smooth path for horizontal orientation
809
- const pathData = this._generateSmoothPath(data, padding, chartWidth, chartHeight, maxValue, xScale, spacing, isReverse, true);
810
- const color = colors[0];
811
-
812
- // Create area path
813
- this._renderAreaPath(svg, pathData, color, isReverse ? width - padding.right : padding.left, animate, true);
814
-
815
- // Add data point circles and labels
816
- data.forEach((point, index) => {
817
- // Changed calculation to place points at edges
818
- const y = padding.top + (index * spacing);
819
- const pointX = point.value * xScale;
820
- const x = isReverse ? width - padding.right - pointX : padding.left + pointX;
821
-
822
- // Data point circle
823
- const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
824
- circle.setAttribute('cx', x.toString());
825
- circle.setAttribute('cy', y.toString());
826
- circle.setAttribute('r', '4');
827
- circle.setAttribute('fill', 'white');
828
- circle.setAttribute('stroke', color);
829
- circle.setAttribute('stroke-width', '2');
830
-
831
- if (animate) {
832
- circle.classList.add('jux-label-animated');
833
- circle.style.animationDelay = `${animationDuration}ms`;
834
- }
835
-
836
- svg.appendChild(circle);
837
-
838
- // Category label
839
- if (showTicksY && showScaleY) {
840
- const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
841
- label.setAttribute('x', (padding.left - 10).toString());
842
- label.setAttribute('y', (y + 4).toString());
843
- label.setAttribute('text-anchor', 'end');
844
- label.setAttribute('fill', '#6b7280');
845
- label.setAttribute('font-size', '11');
846
- label.setAttribute('font-family', 'inherit');
847
- label.textContent = point.label;
848
-
849
- if (animate) {
850
- label.classList.add('jux-label-animated');
851
- label.style.animationDelay = `${index * 100 + 200}ms`;
852
- }
853
-
854
- svg.appendChild(label);
855
- }
856
-
857
- // Value labels
858
- if (showDataLabels) {
859
- const valueLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text');
860
- valueLabel.setAttribute('x', (x + 10).toString());
861
- valueLabel.setAttribute('y', (y + 4).toString());
862
- valueLabel.setAttribute('text-anchor', 'start');
863
- valueLabel.setAttribute('fill', '#374151');
864
- valueLabel.setAttribute('font-size', '11');
865
- valueLabel.setAttribute('font-weight', '600');
866
- valueLabel.setAttribute('font-family', 'inherit');
867
- valueLabel.textContent = point.value.toString();
868
-
869
- if (animate) {
870
- valueLabel.classList.add('jux-label-animated');
871
- valueLabel.style.animationDelay = `${animationDuration}ms`;
872
- }
873
-
874
- svg.appendChild(valueLabel);
875
- }
876
- });
877
- }
878
-
879
- private _generateSmoothPath(
880
- data: AreaChartSmoothDataPoint[],
881
- padding: { top: number, right: number, bottom: number, left: number },
882
- chartWidth: number,
883
- chartHeight: number,
884
- maxValue: number,
885
- scale: number,
886
- spacing: number,
887
- isReverse: boolean,
888
- isHorizontal: boolean
889
- ): string {
890
- if (data.length === 0) return '';
891
-
892
- const points: { x: number, y: number }[] = data.map((point, index) => {
893
- if (isHorizontal) {
894
- // Changed to place points at edges
895
- const y = padding.top + (index * spacing);
896
- const pointX = point.value * scale;
897
- const x = isReverse
898
- ? padding.right + chartWidth - pointX
899
- : padding.left + pointX;
900
- return { x, y };
901
- } else {
902
- // Changed to place points at edges
903
- const x = padding.left + (index * spacing);
904
- const pointY = point.value * scale;
905
- const y = isReverse
906
- ? padding.top + pointY
907
- : padding.top + chartHeight - pointY;
908
- return { x, y };
909
- }
910
- });
911
-
912
- // Start path at first data point
913
- let path = `M ${points[0].x},${points[0].y}`;
914
-
915
- // Generate smooth curve using cubic bezier
916
- for (let i = 0; i < points.length - 1; i++) {
917
- const current = points[i];
918
- const next = points[i + 1];
919
- const prev = i > 0 ? points[i - 1] : current;
920
- const afterNext = i < points.length - 2 ? points[i + 2] : next;
921
-
922
- // Calculate control points for smooth curve
923
- const tension = 0.3;
924
-
925
- if (isHorizontal) {
926
- const cp1x = current.x + (next.x - prev.x) * tension;
927
- const cp1y = current.y;
928
- const cp2x = next.x - (afterNext.x - current.x) * tension;
929
- const cp2y = next.y;
930
- path += ` C ${cp1x},${cp1y} ${cp2x},${cp2y} ${next.x},${next.y}`;
931
- } else {
932
- const cp1x = current.x;
933
- const cp1y = current.y + (next.y - prev.y) * tension;
934
- const cp2x = next.x;
935
- const cp2y = next.y - (afterNext.y - current.y) * tension;
936
- path += ` C ${cp1x},${cp1y} ${cp2x},${cp2y} ${next.x},${next.y}`;
937
- }
938
- }
939
-
940
- return path;
941
- }
942
-
943
- private _renderAreaPath(
944
- svg: SVGSVGElement,
945
- pathData: string,
946
- color: string,
947
- baseline: number,
948
- animate: boolean,
949
- isHorizontal: boolean = false
950
- ): void {
951
- const { styleMode, animationDuration, width, height } = this.state;
952
-
953
- // Extract first and last points from the path
954
- const pathParts = pathData.split(/[MC]/);
955
- const firstPoint = pathParts[1].trim().split(',');
956
- const lastMatch = pathData.match(/(\d+\.?\d*),(\d+\.?\d*)(?!.*\d)/);
957
- const lastPoint = lastMatch ? [lastMatch[1], lastMatch[2]] : firstPoint;
958
-
959
- // Create closed area path - connect last point to baseline, then back to first point at baseline
960
- let areaPath = pathData;
961
- if (isHorizontal) {
962
- // For horizontal: go down to baseline, then back to first point at baseline
963
- areaPath += ` L ${baseline},${lastPoint[1]} L ${baseline},${firstPoint[1]} Z`;
964
- } else {
965
- // For vertical: go down to baseline, then back to first point at baseline
966
- areaPath += ` L ${lastPoint[0]},${baseline} L ${firstPoint[0]},${baseline} Z`;
967
- }
968
-
969
- // Create area element
970
- const area = document.createElementNS('http://www.w3.org/2000/svg', 'path');
971
- area.setAttribute('d', areaPath);
972
-
973
- // Apply style modes
974
- if (styleMode === 'gradient') {
975
- const gradientId = `area-gradient-${this._id}`;
976
- const gradient = document.createElementNS('http://www.w3.org/2000/svg', 'linearGradient');
977
- gradient.setAttribute('id', gradientId);
978
-
979
- if (isHorizontal) {
980
- gradient.setAttribute('x1', '0%');
981
- gradient.setAttribute('y1', '0%');
982
- gradient.setAttribute('x2', '100%');
983
- gradient.setAttribute('y2', '0%');
984
- } else {
985
- gradient.setAttribute('x1', '0%');
986
- gradient.setAttribute('y1', '0%');
987
- gradient.setAttribute('x2', '0%');
988
- gradient.setAttribute('y2', '100%');
989
- }
990
-
991
- const stop1 = document.createElementNS('http://www.w3.org/2000/svg', 'stop');
992
- stop1.setAttribute('offset', '0%');
993
- stop1.setAttribute('stop-color', color);
994
- stop1.setAttribute('stop-opacity', '0.4');
995
-
996
- const stop2 = document.createElementNS('http://www.w3.org/2000/svg', 'stop');
997
- stop2.setAttribute('offset', '100%');
998
- stop2.setAttribute('stop-color', color);
999
- stop2.setAttribute('stop-opacity', '0.05');
1000
-
1001
- gradient.appendChild(stop1);
1002
- gradient.appendChild(stop2);
1003
- svg.appendChild(gradient);
1004
-
1005
- area.setAttribute('fill', `url(#${gradientId})`);
1006
- } else if (styleMode === 'glow') {
1007
- area.setAttribute('fill', color);
1008
- area.setAttribute('fill-opacity', '0.3');
1009
-
1010
- const filterId = `glow-area-${this._id}`;
1011
- const defs = svg.querySelector('defs') || document.createElementNS('http://www.w3.org/2000/svg', 'defs');
1012
- if (!svg.querySelector('defs')) {
1013
- svg.insertBefore(defs, svg.firstChild);
1014
- }
1015
-
1016
- const filter = document.createElementNS('http://www.w3.org/2000/svg', 'filter');
1017
- filter.setAttribute('id', filterId);
1018
- filter.setAttribute('x', '-50%');
1019
- filter.setAttribute('y', '-50%');
1020
- filter.setAttribute('width', '200%');
1021
- filter.setAttribute('height', '200%');
1022
-
1023
- const feGaussianBlur = document.createElementNS('http://www.w3.org/2000/svg', 'feGaussianBlur');
1024
- feGaussianBlur.setAttribute('in', 'SourceGraphic');
1025
- feGaussianBlur.setAttribute('stdDeviation', '6');
1026
- filter.appendChild(feGaussianBlur);
1027
- defs.appendChild(filter);
1028
-
1029
- area.setAttribute('filter', `url(#${filterId})`);
1030
- } else if (styleMode === 'glass') {
1031
- area.setAttribute('fill', color);
1032
- area.setAttribute('fill-opacity', '0.15');
1033
- } else {
1034
- area.setAttribute('fill', color);
1035
- area.setAttribute('fill-opacity', '0.2');
1036
- }
1037
-
1038
- area.setAttribute('stroke', 'none');
1039
-
1040
- if (animate) {
1041
- area.style.opacity = '0';
1042
- area.style.animation = `fadeIn ${animationDuration}ms cubic-bezier(0.4, 0, 0.2, 1) forwards`;
1043
- }
1044
-
1045
- svg.appendChild(area);
1046
-
1047
- // Create stroke line on top (just the curve)
1048
- const line = document.createElementNS('http://www.w3.org/2000/svg', 'path');
1049
- line.setAttribute('d', pathData);
1050
- line.setAttribute('fill', 'none');
1051
- line.setAttribute('stroke', color);
1052
- line.setAttribute('stroke-width', '3');
1053
- line.setAttribute('stroke-linecap', 'round');
1054
- line.setAttribute('stroke-linejoin', 'round');
1055
-
1056
- if (animate) {
1057
- const length = (line as any).getTotalLength?.() || 1000;
1058
- line.style.strokeDasharray = length.toString();
1059
- line.style.strokeDashoffset = length.toString();
1060
- line.style.animation = `drawLine ${animationDuration}ms cubic-bezier(0.4, 0, 0.2, 1) forwards`;
1061
-
1062
- // Add draw animation
1063
- const styleId = `draw-line-animation-${this._id}`;
1064
- if (!document.getElementById(styleId)) {
1065
- const style = document.createElement('style');
1066
- style.id = styleId;
1067
- style.textContent = `
1068
- @keyframes drawLine {
1069
- to {
1070
- stroke-dashoffset: 0;
1071
- }
1072
- }
1073
- `;
1074
- document.head.appendChild(style);
1075
- }
1076
- }
1077
-
1078
- svg.appendChild(line);
1079
- }
1080
-
1081
- private _renderBar(
1082
- svg: SVGSVGElement,
1083
- x: number,
1084
- y: number,
1085
- width: number,
1086
- height: number,
1087
- color: string,
1088
- index: number,
1089
- point: AreaChartSmoothDataPoint,
1090
- labelPosition: 'top' | 'bottom' | 'left' | 'right',
1091
- isHorizontal: boolean = false
1092
- ): void {
1093
- // This method is no longer used for smooth area charts
1094
- // Keeping it for backward compatibility
1095
- }
1096
-
1097
- /* -------------------------
1098
- * Legend and Data Table
1099
- * ------------------------- */
1100
-
1101
- private _createLegend(): HTMLElement {
1102
- const { data, colors, legendOrientation } = this.state;
1103
-
1104
- const legend = document.createElement('div');
1105
- legend.className = 'jux-areachartsmooth-legend';
1106
-
1107
- data.forEach((point, index) => {
1108
- const color = point.color || colors[index % colors.length];
1109
-
1110
- const item = document.createElement('div');
1111
- item.className = 'jux-areachartsmooth-legend-item';
1112
-
1113
- const swatch = document.createElement('div');
1114
- swatch.className = 'jux-areachartsmooth-legend-swatch';
1115
- swatch.style.background = color;
1116
-
1117
- const label = document.createElement('span');
1118
- label.className = 'jux-areachartsmooth-legend-label';
1119
- label.textContent = point.label;
1120
-
1121
- item.appendChild(swatch);
1122
- item.appendChild(label);
1123
- legend.appendChild(item);
1124
- });
1125
-
1126
- return legend;
1127
- }
1128
-
1129
- private _createDataTable(): HTMLElement {
1130
- const { data, xAxisLabel, yAxisLabel } = this.state;
1131
-
1132
- const table = document.createElement('table');
1133
- table.className = 'jux-areachartsmooth-table';
1134
-
1135
- const thead = document.createElement('thead');
1136
- const headerRow = document.createElement('tr');
1137
-
1138
- const columnHeaders = [
1139
- xAxisLabel || 'Label',
1140
- yAxisLabel || 'Value'
1141
- ];
1142
-
1143
- columnHeaders.forEach(text => {
1144
- const th = document.createElement('th');
1145
- th.textContent = text;
1146
- headerRow.appendChild(th);
1147
- });
1148
- thead.appendChild(headerRow);
1149
- table.appendChild(thead);
1150
-
1151
- const tbody = document.createElement('tbody');
1152
- data.forEach(point => {
1153
- const row = document.createElement('tr');
1154
-
1155
- const labelCell = document.createElement('td');
1156
- labelCell.textContent = point.label;
1157
-
1158
- const valueCell = document.createElement('td');
1159
- valueCell.textContent = point.value.toString();
1160
-
1161
- row.appendChild(labelCell);
1162
- row.appendChild(valueCell);
1163
- tbody.appendChild(row);
1164
- });
1165
- table.appendChild(tbody);
1166
-
1167
- return table;
1168
- }
1169
-
1170
- private _lightenColor(color: string, percent: number): string {
1171
- const num = parseInt(color.replace('#', ''), 16);
1172
- const r = Math.min(255, Math.floor((num >> 16) + ((255 - (num >> 16)) * percent / 100)));
1173
- const g = Math.min(255, Math.floor(((num >> 8) & 0x00FF) + ((255 - ((num >> 8) & 0x00FF)) * percent / 100)));
1174
- const b = Math.min(255, Math.floor((num & 0x0000FF) + ((255 - (num & 0x0000FF)) * percent / 100)));
1175
- return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`;
1176
- }
1177
-
1178
- private _applyTheme(themeName: string): void {
1179
- const themes: Record<string, any> = {
1180
- google: googleTheme,
1181
- seriesa: seriesaTheme,
1182
- hr: hrTheme,
1183
- figma: figmaTheme,
1184
- notion: notionTheme,
1185
- chalk: chalkTheme,
1186
- mint: mintTheme
1187
- };
1188
-
1189
- const theme = themes[themeName];
1190
- if (!theme) return;
1191
-
1192
- // Apply colors
1193
- // get one color;
1194
- const randomColor = theme.colors[Math.floor(Math.random() * theme.colors.length)];
1195
- this.state.colors = [randomColor];
1196
-
1197
- // Inject base styles (once)
1198
- const baseStyleId = 'jux-areachartsmooth-base-styles';
1199
- if (!document.getElementById(baseStyleId)) {
1200
- const style = document.createElement('style');
1201
- style.id = baseStyleId;
1202
- style.textContent = this._getBaseStyles();
1203
- document.head.appendChild(style);
1204
- }
1205
-
1206
- // Inject font (once per theme)
1207
- if (theme.font && !document.querySelector(`link[href="${theme.font}"]`)) {
1208
- const link = document.createElement('link');
1209
- link.rel = 'stylesheet';
1210
- link.href = theme.font;
1211
- document.head.appendChild(link);
1212
- }
1213
-
1214
- // Apply theme-specific styles
1215
- const styleId = `jux-areachartsmooth-theme-${themeName}`;
1216
- let styleElement = document.getElementById(styleId) as HTMLStyleElement;
1217
-
1218
- if (!styleElement) {
1219
- styleElement = document.createElement('style');
1220
- styleElement.id = styleId;
1221
- document.head.appendChild(styleElement);
1222
- }
1223
-
1224
- // Generate CSS with theme variables
1225
- const variablesCSS = Object.entries(theme.variables)
1226
- .map(([key, value]) => ` ${key}: ${value};`)
1227
- .join('\n');
1228
-
1229
- styleElement.textContent = `
1230
- .jux-areachartsmooth.theme-${themeName} {
1231
- ${variablesCSS}
1232
- }
1233
- `;
1234
- }
1235
-
1236
- private _applyThemeToWrapper(wrapper: HTMLElement): void {
1237
- if (!this.state.theme) return;
1238
-
1239
- // Remove old theme classes
1240
- wrapper.classList.remove('theme-google', 'theme-seriesa', 'theme-hr', 'theme-figma', 'theme-notion', 'theme-chalk', 'theme-mint');
1241
-
1242
- // Add new theme class
1243
- wrapper.classList.add(`theme-${this.state.theme}`);
1244
- }
1245
-
1246
- private _getBaseStyles(): string {
1247
- return `
1248
- .jux-areachartsmooth {
1249
- font-family: var(--chart-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
1250
- display: inline-block;
1251
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
1252
- border-radius: 12px;
1253
- background: white;
1254
- padding: 24px;
1255
- }
1256
-
1257
- .jux-areachartsmooth-title {
1258
- margin: 0 0 0.5rem 0;
1259
- font-size: 1.25rem;
1260
- font-weight: 600;
1261
- font-family: inherit;
1262
- }
1263
-
1264
- .jux-areachartsmooth-subtitle {
1265
- margin: 0 0 1rem 0;
1266
- font-size: 0.875rem;
1267
- color: #6b7280;
1268
- font-family: inherit;
1269
- }
1270
-
1271
- .jux-areachartsmooth-legend {
1272
- display: flex;
1273
- flex-wrap: wrap;
1274
- gap: 1rem;
1275
- margin-top: 1rem;
1276
- justify-content: center;
1277
- }
1278
-
1279
- .jux-areachartsmooth-legend-item {
1280
- display: flex;
1281
- align-items: center;
1282
- gap: 0.5rem;
1283
- }
1284
-
1285
- .jux-areachartsmooth-legend-swatch {
1286
- width: 12px;
1287
- height: 12px;
1288
- border-radius: 2px;
1289
- }
1290
-
1291
- .jux-areachartsmooth-legend-label {
1292
- font-size: 0.875rem;
1293
- color: #374151;
1294
- font-family: inherit;
1295
- }
1296
-
1297
- .jux-areachartsmooth-table {
1298
- width: 100%;
1299
- margin-top: 1rem;
1300
- border-collapse: collapse;
1301
- font-size: 0.875rem;
1302
- font-family: inherit;
1303
-
1304
- }
1305
-
1306
- .jux-areachartsmooth-table thead th {
1307
- text-align: left;
1308
- padding: 0.5rem;
1309
- border-bottom: 2px solid #e5e7eb;
1310
- font-weight: 600;
1311
- text-align: center;
1312
- }
1313
-
1314
- .jux-areachartsmooth-table tbody td {
1315
- padding: 0.5rem;
1316
- border-bottom: 1px solid #f3f4f6;
1317
- text-align: center;
1318
- }
1319
-
1320
- .jux-areachartsmooth-svg {
1321
- font-family: inherit;
1322
- }
1323
- `;
1324
- }
1325
-
1326
- render(targetId?: string | HTMLElement): this {
1327
- // Apply theme first if set
1328
- if (this.state.theme) {
1329
- this._applyTheme(this.state.theme);
1330
- }
1331
-
1332
- let container: HTMLElement;
1333
-
1334
- if (targetId) {
1335
- if (targetId instanceof HTMLElement) {
1336
- container = targetId;
1337
- } else {
1338
- const target = document.querySelector(targetId);
1339
- if (!target || !(target instanceof HTMLElement)) {
1340
- throw new Error(`AreaChartSmooth: Target element "${targetId}" not found`);
1341
- }
1342
- container = target;
1343
- }
1344
- } else {
1345
- container = getOrCreateContainer(this._id);
1346
- }
1347
-
1348
- this.container = container;
1349
- const { class: className, style } = this.state;
1350
-
1351
- const wrapper = document.createElement('div');
1352
- wrapper.id = this._id;
1353
- wrapper.className = 'jux-areachartsmooth';
1354
-
1355
- // Add theme class
1356
- if (this.state.theme) {
1357
- wrapper.classList.add(`theme-${this.state.theme}`);
1358
- }
1359
-
1360
- // Add custom class
1361
- if (className) {
1362
- wrapper.classList.add(...className.split(' '));
1363
- }
1364
-
1365
- if (style) {
1366
- wrapper.setAttribute('style', style);
1367
- }
1368
-
1369
- container.appendChild(wrapper);
1370
-
1371
- // Build chart content
1372
- this._buildChart(wrapper);
1373
-
1374
- return this;
1375
- }
1376
- }
1377
-
1378
- export function areachartsmooth(id: string, options: AreaChartSmoothOptions = {}): AreaChartSmooth {
1379
- return new AreaChartSmooth(id, options);
1380
- }