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,1250 +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 BarChartDataPoint {
17
- label: string;
18
- value: number;
19
- color?: string;
20
- }
21
-
22
- /**
23
- * Bar chart options
24
- */
25
- export interface BarChartOptions {
26
- data?: BarChartDataPoint[];
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' | 'outline' | 'dashed' | 'glow' | 'glass';
52
- borderRadius?: number;
53
- }
54
-
55
- /**
56
- * Bar chart state
57
- */
58
- type BarChartState = {
59
- data: BarChartDataPoint[];
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' | 'outline' | 'dashed' | 'glow' | 'glass';
85
- borderRadius: number;
86
- };
87
-
88
- /**
89
- * Bar chart component - Simple SVG-based bar chart
90
- *
91
- * Usage:
92
- * jux.barchart('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 BarChart {
103
- state: BarChartState;
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: BarChartOptions = {}) {
114
- this._id = id;
115
- this.id = id;
116
-
117
- const defaultColors = [
118
- '#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6',
119
- '#ec4899', '#06b6d4', '#f97316', '#84cc16', '#6366f1'
120
- ];
121
-
122
- this.state = {
123
- data: options.data ?? [],
124
- title: options.title ?? '',
125
- subtitle: options.subtitle ?? '',
126
- xAxisLabel: options.xAxisLabel ?? '',
127
- yAxisLabel: options.yAxisLabel ?? '',
128
- showTicksX: options.showTicksX ?? true,
129
- showTicksY: options.showTicksY ?? true,
130
- showScaleX: options.showScaleX ?? true,
131
- showScaleY: options.showScaleY ?? true,
132
- scaleXUnit: options.scaleXUnit ?? '',
133
- scaleYUnit: options.scaleYUnit ?? '',
134
- showLegend: options.showLegend ?? false,
135
- legendOrientation: options.legendOrientation ?? 'horizontal',
136
- showDataTable: options.showDataTable ?? false,
137
- showDataLabels: options.showDataLabels ?? true,
138
- animate: options.animate ?? true,
139
- animationDuration: options.animationDuration ?? 800,
140
- chartOrientation: options.chartOrientation ?? 'vertical', // NEW
141
- chartDirection: options.chartDirection ?? 'normal', // NEW
142
- width: options.width ?? 600,
143
- height: options.height ?? 400,
144
- colors: options.colors ?? defaultColors,
145
- class: options.class ?? '',
146
- style: options.style ?? '',
147
- theme: options.theme,
148
- styleMode: options.styleMode ?? 'default',
149
- borderRadius: options.borderRadius ?? 4
150
- };
151
- }
152
-
153
- /* -------------------------
154
- * State Binding Methods
155
- * ------------------------- */
156
-
157
- /**
158
- * Bind theme to reactive state
159
- */
160
- bindTheme(stateObj: State<string>): this {
161
- this._boundTheme = stateObj;
162
-
163
- stateObj.subscribe((val) => {
164
- this.theme(val as any);
165
- });
166
-
167
- return this;
168
- }
169
-
170
- /**
171
- * Bind styleMode to reactive state
172
- */
173
- bindStyleMode(stateObj: State<string>): this {
174
- this._boundStyleMode = stateObj;
175
-
176
- stateObj.subscribe((val) => {
177
- this.styleMode(val as any);
178
- });
179
-
180
- return this;
181
- }
182
-
183
- /**
184
- * Bind borderRadius to reactive state
185
- */
186
- bindBorderRadius(stateObj: State<number>): this {
187
- this._boundBorderRadius = stateObj;
188
-
189
- stateObj.subscribe((val) => {
190
- this.borderRadius(val);
191
- });
192
-
193
- return this;
194
- }
195
-
196
- /* -------------------------
197
- * Fluent API
198
- * ------------------------- */
199
-
200
- data(value: BarChartDataPoint[]): this {
201
- this.state.data = value;
202
- this._updateChart();
203
- return this;
204
- }
205
-
206
- title(value: string): this {
207
- this.state.title = value;
208
- this._updateChart();
209
- return this;
210
- }
211
-
212
- subtitle(value: string): this {
213
- this.state.subtitle = value;
214
- this._updateChart();
215
- return this;
216
- }
217
-
218
- xAxisLabel(value: string): this {
219
- this.state.xAxisLabel = value;
220
- this._updateChart();
221
- return this;
222
- }
223
-
224
- yAxisLabel(value: string): this {
225
- this.state.yAxisLabel = value;
226
- this._updateChart();
227
- return this;
228
- }
229
-
230
- showTicksX(value: boolean): this {
231
- this.state.showTicksX = value;
232
- this._updateChart();
233
- return this;
234
- }
235
-
236
- showTicksY(value: boolean): this {
237
- this.state.showTicksY = value;
238
- this._updateChart();
239
- return this;
240
- }
241
-
242
- showScaleX(value: boolean): this {
243
- this.state.showScaleX = value;
244
- this._updateChart();
245
- return this;
246
- }
247
-
248
- showScaleY(value: boolean): this {
249
- this.state.showScaleY = value;
250
- this._updateChart();
251
- return this;
252
- }
253
-
254
- scaleXUnit(value: string): this {
255
- this.state.scaleXUnit = value;
256
- this._updateChart();
257
- return this;
258
- }
259
-
260
- scaleYUnit(value: string): this {
261
- this.state.scaleYUnit = value;
262
- this._updateChart();
263
- return this;
264
- }
265
-
266
- showLegend(value: boolean): this {
267
- this.state.showLegend = value;
268
- this._updateChart();
269
- return this;
270
- }
271
-
272
- legendOrientation(value: 'horizontal' | 'vertical'): this {
273
- this.state.legendOrientation = value;
274
- this._updateChart();
275
- return this;
276
- }
277
-
278
- showDataTable(value: boolean): this {
279
- this.state.showDataTable = value;
280
- this._updateChart();
281
- return this;
282
- }
283
-
284
- /**
285
- * Show/hide value labels on bars
286
- */
287
- showDataLabels(value: boolean): this {
288
- this.state.showDataLabels = value;
289
- this._updateChart();
290
- return this;
291
- }
292
-
293
- /**
294
- * Enable/disable bar grow animation
295
- */
296
- animate(value: boolean): this {
297
- this.state.animate = value;
298
- this._updateChart();
299
- return this;
300
- }
301
-
302
- /**
303
- * Set animation duration in milliseconds
304
- */
305
- animationDuration(value: number): this {
306
- this.state.animationDuration = value;
307
- this._updateChart();
308
- return this;
309
- }
310
-
311
- /**
312
- * Set chart orientation (vertical bars or horizontal bars)
313
- */
314
- chartOrientation(value: 'vertical' | 'horizontal'): this {
315
- this.state.chartOrientation = value;
316
- this._updateChart();
317
- return this;
318
- }
319
-
320
- /**
321
- * Set chart direction (normal or reverse)
322
- * For vertical: normal = bottom-to-top, reverse = top-to-bottom
323
- * For horizontal: normal = left-to-right, reverse = right-to-left
324
- */
325
- chartDirection(value: 'normal' | 'reverse'): this {
326
- this.state.chartDirection = value;
327
- this._updateChart();
328
- return this;
329
- }
330
-
331
- width(value: number): this {
332
- this.state.width = value;
333
- this._updateChart();
334
- return this;
335
- }
336
-
337
- height(value: number): this {
338
- this.state.height = value;
339
- this._updateChart();
340
- return this;
341
- }
342
-
343
- colors(value: string[]): this {
344
- this.state.colors = value;
345
- this._updateChart();
346
- return this;
347
- }
348
-
349
- class(value: string): this {
350
- this.state.class = value;
351
- return this;
352
- }
353
-
354
- style(value: string): this {
355
- this.state.style = value;
356
- return this;
357
- }
358
-
359
- /**
360
- * Set chart theme
361
- */
362
- theme(value: 'google' | 'seriesa' | 'hr' | 'figma' | 'notion' | 'chalk' | 'mint'): this {
363
- this.state.theme = value;
364
- this._applyTheme(value);
365
- this._updateChart();
366
- return this;
367
- }
368
-
369
- /**
370
- * Set bar style mode
371
- */
372
- styleMode(value: 'default' | 'gradient' | 'outline' | 'dashed' | 'glow' | 'glass'): this {
373
- this.state.styleMode = value;
374
- this._updateChart();
375
- return this;
376
- }
377
-
378
- /**
379
- * Set border radius for bars (0 = sharp corners, higher = rounder)
380
- */
381
- borderRadius(value: number): this {
382
- this.state.borderRadius = value;
383
- this._updateChart();
384
- return this;
385
- }
386
-
387
- /* -------------------------
388
- * Update chart
389
- * ------------------------- */
390
-
391
- private _updateChart(): void {
392
- if (!this.container) return;
393
-
394
- // Find the wrapper div
395
- const wrapper = this.container.querySelector(`#${this._id}`) as HTMLElement;
396
- if (!wrapper) return;
397
-
398
- // Clear and rebuild
399
- wrapper.innerHTML = '';
400
- this._buildChart(wrapper);
401
-
402
- // Reapply theme after rebuild
403
- if (this.state.theme) {
404
- this._applyThemeToWrapper(wrapper);
405
- }
406
- }
407
-
408
- private _buildChart(wrapper: HTMLElement): void {
409
- const { data, title, subtitle, width, height, showLegend, showDataTable } = this.state;
410
-
411
- // Title
412
- if (title) {
413
- const titleEl = document.createElement('h3');
414
- titleEl.className = 'jux-barchart-title';
415
- titleEl.textContent = title;
416
- wrapper.appendChild(titleEl);
417
- }
418
-
419
- // Subtitle
420
- if (subtitle) {
421
- const subtitleEl = document.createElement('p');
422
- subtitleEl.className = 'jux-barchart-subtitle';
423
- subtitleEl.textContent = subtitle;
424
- wrapper.appendChild(subtitleEl);
425
- }
426
-
427
- // SVG Chart
428
- const svg = this._createSVG();
429
- wrapper.appendChild(svg);
430
-
431
- // Legend
432
- if (showLegend) {
433
- const legend = this._createLegend();
434
- wrapper.appendChild(legend);
435
- }
436
-
437
- // Data Table
438
- if (showDataTable) {
439
- const table = this._createDataTable();
440
- wrapper.appendChild(table);
441
- }
442
- }
443
-
444
- private _createSVG(): SVGSVGElement {
445
- const {
446
- data, width, height, colors, xAxisLabel, yAxisLabel,
447
- showTicksX, showTicksY, showScaleX, showScaleY, scaleYUnit,
448
- styleMode, borderRadius, showDataLabels, animate, animationDuration,
449
- chartOrientation, chartDirection
450
- } = this.state;
451
-
452
- if (!data.length) {
453
- const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
454
- svg.setAttribute('width', width.toString());
455
- svg.setAttribute('height', height.toString());
456
- svg.setAttribute('class', 'jux-barchart-svg');
457
- return svg;
458
- }
459
-
460
- const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
461
- svg.setAttribute('width', width.toString());
462
- svg.setAttribute('height', height.toString());
463
- svg.setAttribute('class', 'jux-barchart-svg');
464
-
465
- // Add animation styles to SVG
466
- if (animate) {
467
- const animationId = `bar-grow-${this._id}`;
468
- const style = document.createElementNS('http://www.w3.org/2000/svg', 'style');
469
-
470
- // Different animation based on orientation and direction
471
- let transformOrigin = 'bottom';
472
- let scaleAxis = 'scaleY';
473
-
474
- if (chartOrientation === 'horizontal') {
475
- if (chartDirection === 'normal') {
476
- transformOrigin = 'left';
477
- scaleAxis = 'scaleX';
478
- } else {
479
- transformOrigin = 'right';
480
- scaleAxis = 'scaleX';
481
- }
482
- } else {
483
- if (chartDirection === 'reverse') {
484
- transformOrigin = 'top';
485
- scaleAxis = 'scaleY';
486
- }
487
- }
488
-
489
- style.textContent = `
490
- @keyframes ${animationId} {
491
- from {
492
- transform: ${scaleAxis}(0);
493
- opacity: 0;
494
- }
495
- to {
496
- transform: ${scaleAxis}(1);
497
- opacity: 1;
498
- }
499
- }
500
- .jux-bar-animated {
501
- transform-origin: ${transformOrigin};
502
- animation: ${animationId} ${animationDuration}ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
503
- }
504
- .jux-label-animated {
505
- opacity: 0;
506
- animation: fadeIn ${animationDuration}ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
507
- }
508
- @keyframes fadeIn {
509
- from { opacity: 0; }
510
- to { opacity: 1; }
511
- }
512
- `;
513
- svg.appendChild(style);
514
- }
515
-
516
- // Calculate dimensions
517
- const padding = { top: 40, right: 40, bottom: 60, left: 60 };
518
- const chartWidth = width - padding.left - padding.right;
519
- const chartHeight = height - padding.top - padding.bottom;
520
-
521
- const maxValue = Math.max(...data.map(d => d.value));
522
-
523
- // Determine if we're working with vertical or horizontal layout
524
- const isVertical = chartOrientation === 'vertical';
525
- const isReverse = chartDirection === 'reverse';
526
-
527
- if (isVertical) {
528
- // VERTICAL BARS (original logic with direction support)
529
- this._renderVerticalBars(svg, data, colors, padding, chartWidth, chartHeight, maxValue, isReverse);
530
- } else {
531
- // HORIZONTAL BARS (new logic)
532
- this._renderHorizontalBars(svg, data, colors, padding, chartWidth, chartHeight, maxValue, isReverse);
533
- }
534
-
535
- return svg;
536
- }
537
-
538
- private _renderVerticalBars(
539
- svg: SVGSVGElement,
540
- data: BarChartDataPoint[],
541
- colors: string[],
542
- padding: { top: number, right: number, bottom: number, left: number },
543
- chartWidth: number,
544
- chartHeight: number,
545
- maxValue: number,
546
- isReverse: boolean
547
- ): void {
548
- const { width, height, xAxisLabel, yAxisLabel, showTicksX, showTicksY, showScaleX, showScaleY, scaleYUnit, styleMode, borderRadius, showDataLabels, animate, animationDuration } = this.state;
549
-
550
- const yScale = chartHeight / maxValue;
551
- const barWidth = chartWidth / data.length;
552
- const barGap = barWidth * 0.2;
553
- const actualBarWidth = barWidth - barGap;
554
-
555
- // Y-axis
556
- if (showScaleY) {
557
- const yAxis = document.createElementNS('http://www.w3.org/2000/svg', 'line');
558
- yAxis.setAttribute('x1', padding.left.toString());
559
- yAxis.setAttribute('y1', padding.top.toString());
560
- yAxis.setAttribute('x2', padding.left.toString());
561
- yAxis.setAttribute('y2', (height - padding.bottom).toString());
562
- yAxis.setAttribute('stroke', '#9ca3af');
563
- yAxis.setAttribute('stroke-width', '2');
564
- svg.appendChild(yAxis);
565
-
566
- // Y-axis label
567
- if (yAxisLabel && showTicksY) {
568
- const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
569
- label.setAttribute('x', '20');
570
- label.setAttribute('y', (padding.top + chartHeight / 2).toString());
571
- label.setAttribute('text-anchor', 'middle');
572
- label.setAttribute('transform', `rotate(-90, 20, ${padding.top + chartHeight / 2})`);
573
- label.setAttribute('fill', '#6b7280');
574
- label.setAttribute('font-size', '12');
575
- label.setAttribute('font-weight', '500');
576
- label.setAttribute('font-family', 'inherit');
577
- label.textContent = yAxisLabel;
578
- svg.appendChild(label);
579
- }
580
-
581
- // Y-axis ticks
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
- // Grid line
591
- const gridLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
592
- gridLine.setAttribute('x1', padding.left.toString());
593
- gridLine.setAttribute('y1', y.toString());
594
- gridLine.setAttribute('x2', (width - padding.right).toString());
595
- gridLine.setAttribute('y2', y.toString());
596
- gridLine.setAttribute('stroke', '#e5e7eb');
597
- gridLine.setAttribute('stroke-width', '1');
598
- gridLine.setAttribute('stroke-dasharray', '4,4');
599
- svg.appendChild(gridLine);
600
-
601
- // Tick label
602
- const tickLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text');
603
- tickLabel.setAttribute('x', (padding.left - 10).toString());
604
- tickLabel.setAttribute('y', (y + 4).toString());
605
- tickLabel.setAttribute('text-anchor', 'end');
606
- tickLabel.setAttribute('fill', '#6b7280');
607
- tickLabel.setAttribute('font-size', '11');
608
- tickLabel.setAttribute('font-family', 'inherit');
609
- tickLabel.textContent = Math.round(value).toString() + (scaleYUnit || '');
610
- svg.appendChild(tickLabel);
611
- }
612
- }
613
- }
614
-
615
- // X-axis
616
- if (showScaleX) {
617
- const xAxis = document.createElementNS('http://www.w3.org/2000/svg', 'line');
618
- const axisY = isReverse ? padding.top : height - padding.bottom;
619
- xAxis.setAttribute('x1', padding.left.toString());
620
- xAxis.setAttribute('y1', axisY.toString());
621
- xAxis.setAttribute('x2', (width - padding.right).toString());
622
- xAxis.setAttribute('y2', axisY.toString());
623
- xAxis.setAttribute('stroke', '#9ca3af');
624
- xAxis.setAttribute('stroke-width', '2');
625
- svg.appendChild(xAxis);
626
-
627
- // X-axis label
628
- if (xAxisLabel && showTicksX) {
629
- const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
630
- label.setAttribute('x', (padding.left + chartWidth / 2).toString());
631
- label.setAttribute('y', (height - 15).toString());
632
- label.setAttribute('text-anchor', 'middle');
633
- label.setAttribute('fill', '#6b7280');
634
- label.setAttribute('font-size', '12');
635
- label.setAttribute('font-weight', '500');
636
- label.setAttribute('font-family', 'inherit');
637
- label.textContent = xAxisLabel;
638
- svg.appendChild(label);
639
- }
640
- }
641
-
642
- // Bars
643
- data.forEach((point, index) => {
644
- const x = padding.left + (index * barWidth) + (barGap / 2);
645
- const barHeight = point.value * yScale;
646
- const y = isReverse
647
- ? padding.top
648
- : height - padding.bottom - barHeight;
649
- const color = point.color || colors[index % colors.length];
650
-
651
- this._renderBar(svg, x, y, actualBarWidth, barHeight, color, index, point, isReverse ? 'bottom' : 'top');
652
- });
653
- }
654
-
655
- private _renderHorizontalBars(
656
- svg: SVGSVGElement,
657
- data: BarChartDataPoint[],
658
- colors: string[],
659
- padding: { top: number, right: number, bottom: number, left: number },
660
- chartWidth: number,
661
- chartHeight: number,
662
- maxValue: number,
663
- isReverse: boolean
664
- ): void {
665
- const { width, height, xAxisLabel, yAxisLabel, showTicksX, showTicksY, showScaleX, showScaleY, scaleXUnit, styleMode, borderRadius, showDataLabels, animate, animationDuration } = this.state;
666
-
667
- const xScale = chartWidth / maxValue;
668
- const barHeight = chartHeight / data.length;
669
- const barGap = barHeight * 0.2;
670
- const actualBarHeight = barHeight - barGap;
671
-
672
- // X-axis (now the value axis) - use yAxisLabel since values are horizontal
673
- if (showScaleX) {
674
- const xAxis = document.createElementNS('http://www.w3.org/2000/svg', 'line');
675
- const axisX = isReverse ? width - padding.right : padding.left;
676
- xAxis.setAttribute('x1', axisX.toString());
677
- xAxis.setAttribute('y1', padding.top.toString());
678
- xAxis.setAttribute('x2', axisX.toString());
679
- xAxis.setAttribute('y2', (height - padding.bottom).toString());
680
- xAxis.setAttribute('stroke', '#9ca3af');
681
- xAxis.setAttribute('stroke-width', '2');
682
- svg.appendChild(xAxis);
683
-
684
- // X-axis label (use yAxisLabel for values in horizontal mode)
685
- if (yAxisLabel && showTicksX) {
686
- const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
687
- label.setAttribute('x', (padding.left + chartWidth / 2).toString());
688
- label.setAttribute('y', (height - 15).toString());
689
- label.setAttribute('text-anchor', 'middle');
690
- label.setAttribute('fill', '#6b7280');
691
- label.setAttribute('font-size', '12');
692
- label.setAttribute('font-weight', '500');
693
- label.setAttribute('font-family', 'inherit');
694
- label.textContent = yAxisLabel;
695
- svg.appendChild(label);
696
- }
697
-
698
- // X-axis ticks
699
- if (showTicksX) {
700
- const numTicks = 5;
701
- for (let i = 0; i <= numTicks; i++) {
702
- const value = (maxValue / numTicks) * i;
703
- const x = isReverse
704
- ? width - padding.right - (value * xScale)
705
- : padding.left + (value * xScale);
706
-
707
- // Grid line
708
- const gridLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
709
- gridLine.setAttribute('x1', x.toString());
710
- gridLine.setAttribute('y1', padding.top.toString());
711
- gridLine.setAttribute('x2', x.toString());
712
- gridLine.setAttribute('y2', (height - padding.bottom).toString());
713
- gridLine.setAttribute('stroke', '#e5e7eb');
714
- gridLine.setAttribute('stroke-width', '1');
715
- gridLine.setAttribute('stroke-dasharray', '4,4');
716
- svg.appendChild(gridLine);
717
-
718
- // Tick label (use scaleYUnit since values are on x-axis now)
719
- const tickLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text');
720
- tickLabel.setAttribute('x', x.toString());
721
- tickLabel.setAttribute('y', (height - padding.bottom + 20).toString());
722
- tickLabel.setAttribute('text-anchor', 'middle');
723
- tickLabel.setAttribute('fill', '#6b7280');
724
- tickLabel.setAttribute('font-size', '11');
725
- tickLabel.setAttribute('font-family', 'inherit');
726
- tickLabel.textContent = Math.round(value).toString() + (scaleXUnit || '');
727
- svg.appendChild(tickLabel);
728
- }
729
- }
730
- }
731
-
732
- // Y-axis (now the category axis) - use xAxisLabel since categories are vertical
733
- if (showScaleY) {
734
- const yAxis = document.createElementNS('http://www.w3.org/2000/svg', 'line');
735
- yAxis.setAttribute('x1', padding.left.toString());
736
- yAxis.setAttribute('y1', padding.top.toString());
737
- yAxis.setAttribute('x2', padding.left.toString());
738
- yAxis.setAttribute('y2', (height - padding.bottom).toString());
739
- yAxis.setAttribute('stroke', '#9ca3af');
740
- yAxis.setAttribute('stroke-width', '2');
741
- svg.appendChild(yAxis);
742
-
743
- // Y-axis label (use xAxisLabel for categories in horizontal mode)
744
- if (xAxisLabel && showTicksY) {
745
- const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
746
- label.setAttribute('x', '20');
747
- label.setAttribute('y', (padding.top + chartHeight / 2).toString());
748
- label.setAttribute('text-anchor', 'middle');
749
- label.setAttribute('transform', `rotate(-90, 20, ${padding.top + chartHeight / 2})`);
750
- label.setAttribute('fill', '#6b7280');
751
- label.setAttribute('font-size', '12');
752
- label.setAttribute('font-weight', '500');
753
- label.setAttribute('font-family', 'inherit');
754
- label.textContent = xAxisLabel;
755
- svg.appendChild(label);
756
- }
757
- }
758
-
759
- // Bars
760
- data.forEach((point, index) => {
761
- const y = padding.top + (index * barHeight) + (barGap / 2);
762
- const barWidth = point.value * xScale;
763
- const x = isReverse
764
- ? width - padding.right - barWidth
765
- : padding.left;
766
- const color = point.color || colors[index % colors.length];
767
-
768
- this._renderBar(svg, x, y, barWidth, actualBarHeight, color, index, point, isReverse ? 'right' : 'left', true);
769
-
770
- // Category label
771
- if (showTicksY && showScaleY) {
772
- const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
773
- label.setAttribute('x', (padding.left - 10).toString());
774
- label.setAttribute('y', (y + actualBarHeight / 2 + 4).toString());
775
- label.setAttribute('text-anchor', 'end');
776
- label.setAttribute('fill', '#6b7280');
777
- label.setAttribute('font-size', '11');
778
- label.setAttribute('font-family', 'inherit');
779
- label.textContent = point.label;
780
-
781
- if (animate) {
782
- label.classList.add('jux-label-animated');
783
- label.style.animationDelay = `${index * 100 + 200}ms`;
784
- }
785
-
786
- svg.appendChild(label);
787
- }
788
- });
789
- }
790
-
791
- private _renderBar(
792
- svg: SVGSVGElement,
793
- x: number,
794
- y: number,
795
- width: number,
796
- height: number,
797
- color: string,
798
- index: number,
799
- point: BarChartDataPoint,
800
- labelPosition: 'top' | 'bottom' | 'left' | 'right',
801
- isHorizontal: boolean = false
802
- ): void {
803
- const { styleMode, borderRadius, showDataLabels, animate, animationDuration } = this.state;
804
-
805
- const shouldUseGroup = (styleMode === 'glass' || styleMode === 'glow') && animate;
806
- let animationTarget: SVGElement;
807
-
808
- if (shouldUseGroup) {
809
- const group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
810
- animationTarget = group;
811
- if (animate) {
812
- group.classList.add('jux-bar-animated');
813
- group.style.animationDelay = `${index * 100}ms`;
814
- }
815
- svg.appendChild(group);
816
- }
817
-
818
- const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
819
- rect.setAttribute('x', x.toString());
820
- rect.setAttribute('y', y.toString());
821
- rect.setAttribute('width', width.toString());
822
- rect.setAttribute('height', height.toString());
823
- rect.setAttribute('rx', borderRadius.toString());
824
- rect.setAttribute('ry', borderRadius.toString());
825
-
826
- if (!shouldUseGroup && animate) {
827
- rect.classList.add('jux-bar-animated');
828
- rect.style.animationDelay = `${index * 100}ms`;
829
- }
830
-
831
- // Apply style modes (same as before)
832
- if (styleMode === 'gradient') {
833
- const gradientId = `gradient-${this._id}-${index}`;
834
- const gradient = document.createElementNS('http://www.w3.org/2000/svg', 'linearGradient');
835
- gradient.setAttribute('id', gradientId);
836
-
837
- if (isHorizontal) {
838
- gradient.setAttribute('x1', '0%');
839
- gradient.setAttribute('y1', '0%');
840
- gradient.setAttribute('x2', '100%');
841
- gradient.setAttribute('y2', '0%');
842
- } else {
843
- gradient.setAttribute('x1', '0%');
844
- gradient.setAttribute('y1', '0%');
845
- gradient.setAttribute('x2', '0%');
846
- gradient.setAttribute('y2', '100%');
847
- }
848
-
849
- const stop1 = document.createElementNS('http://www.w3.org/2000/svg', 'stop');
850
- stop1.setAttribute('offset', '0%');
851
- stop1.setAttribute('stop-color', color);
852
- stop1.setAttribute('stop-opacity', '1');
853
-
854
- const stop2 = document.createElementNS('http://www.w3.org/2000/svg', 'stop');
855
- stop2.setAttribute('offset', '100%');
856
- stop2.setAttribute('stop-color', this._lightenColor(color, 40));
857
- stop2.setAttribute('stop-opacity', '1');
858
-
859
- gradient.appendChild(stop1);
860
- gradient.appendChild(stop2);
861
- svg.appendChild(gradient);
862
-
863
- rect.setAttribute('fill', `url(#${gradientId})`);
864
- } else if (styleMode === 'outline') {
865
- rect.setAttribute('fill', 'transparent');
866
- rect.setAttribute('stroke', color);
867
- rect.setAttribute('stroke-width', '3');
868
- } else if (styleMode === 'dashed') {
869
- rect.setAttribute('fill', this._lightenColor(color, 60));
870
- rect.setAttribute('stroke', color);
871
- rect.setAttribute('stroke-width', '2');
872
- rect.setAttribute('stroke-dasharray', '8,4');
873
- } else if (styleMode === 'glow') {
874
- rect.setAttribute('fill', color);
875
- const filterId = `glow-${this._id}-${index}`;
876
- const defs = svg.querySelector('defs') || document.createElementNS('http://www.w3.org/2000/svg', 'defs');
877
- if (!svg.querySelector('defs')) {
878
- svg.insertBefore(defs, svg.firstChild);
879
- }
880
-
881
- const filter = document.createElementNS('http://www.w3.org/2000/svg', 'filter');
882
- filter.setAttribute('id', filterId);
883
- filter.setAttribute('x', '-50%');
884
- filter.setAttribute('y', '-50%');
885
- filter.setAttribute('width', '200%');
886
- filter.setAttribute('height', '200%');
887
-
888
- const feGaussianBlur = document.createElementNS('http://www.w3.org/2000/svg', 'feGaussianBlur');
889
- feGaussianBlur.setAttribute('in', 'SourceGraphic');
890
- feGaussianBlur.setAttribute('stdDeviation', '4');
891
- feGaussianBlur.setAttribute('result', 'blur');
892
-
893
- const feFlood = document.createElementNS('http://www.w3.org/2000/svg', 'feFlood');
894
- feFlood.setAttribute('flood-color', color);
895
- feFlood.setAttribute('result', 'color');
896
-
897
- const feComposite = document.createElementNS('http://www.w3.org/2000/svg', 'feComposite');
898
- feComposite.setAttribute('in', 'color');
899
- feComposite.setAttribute('in2', 'blur');
900
- feComposite.setAttribute('operator', 'in');
901
- feComposite.setAttribute('result', 'glow');
902
-
903
- const feMerge = document.createElementNS('http://www.w3.org/2000/svg', 'feMerge');
904
- const feMergeNode1 = document.createElementNS('http://www.w3.org/2000/svg', 'feMergeNode');
905
- feMergeNode1.setAttribute('in', 'glow');
906
- const feMergeNode2 = document.createElementNS('http://www.w3.org/2000/svg', 'feMergeNode');
907
- feMergeNode2.setAttribute('in', 'SourceGraphic');
908
- feMerge.appendChild(feMergeNode1);
909
- feMerge.appendChild(feMergeNode2);
910
-
911
- filter.appendChild(feGaussianBlur);
912
- filter.appendChild(feFlood);
913
- filter.appendChild(feComposite);
914
- filter.appendChild(feMerge);
915
- defs.appendChild(filter);
916
-
917
- rect.setAttribute('filter', `url(#${filterId})`);
918
- } else if (styleMode === 'glass') {
919
- rect.setAttribute('fill', color);
920
- rect.setAttribute('fill-opacity', '0.6');
921
- rect.setAttribute('stroke', color);
922
- rect.setAttribute('stroke-width', '2');
923
- rect.setAttribute('stroke-opacity', '0.8');
924
- } else {
925
- rect.setAttribute('fill', color);
926
- }
927
-
928
- if (shouldUseGroup) {
929
- animationTarget.appendChild(rect);
930
- } else {
931
- svg.appendChild(rect);
932
- }
933
-
934
- // Value labels
935
- if (showDataLabels) {
936
- const valueLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text');
937
-
938
- if (isHorizontal) {
939
- // For horizontal bars, place label in the middle of the bar
940
- valueLabel.setAttribute('x', (x + width / 2).toString());
941
- valueLabel.setAttribute('y', (y + height / 2 + 4).toString());
942
- valueLabel.setAttribute('text-anchor', 'middle');
943
- valueLabel.setAttribute('fill', '#ffffff');
944
- valueLabel.setAttribute('font-weight', '700');
945
- } else {
946
- valueLabel.setAttribute('x', (x + width / 2).toString());
947
- if (labelPosition === 'top') {
948
- valueLabel.setAttribute('y', (y - 5).toString());
949
- } else {
950
- valueLabel.setAttribute('y', (y + height + 15).toString());
951
- }
952
- valueLabel.setAttribute('text-anchor', 'middle');
953
- valueLabel.setAttribute('fill', '#374151');
954
- valueLabel.setAttribute('font-weight', '600');
955
- }
956
-
957
- valueLabel.setAttribute('font-size', '11');
958
- valueLabel.setAttribute('font-family', 'inherit');
959
- valueLabel.textContent = point.value.toString();
960
-
961
- if (animate) {
962
- valueLabel.classList.add('jux-label-animated');
963
- valueLabel.style.animationDelay = `${index * 100 + animationDuration - 200}ms`;
964
- }
965
-
966
- svg.appendChild(valueLabel);
967
- }
968
- }
969
-
970
- /* -------------------------
971
- * Legend and Data Table
972
- * ------------------------- */
973
-
974
- private _createLegend(): HTMLElement {
975
- const { data, colors, legendOrientation } = this.state;
976
-
977
- const legend = document.createElement('div');
978
- legend.className = 'jux-barchart-legend';
979
-
980
- data.forEach((point, index) => {
981
- const color = point.color || colors[index % colors.length];
982
-
983
- const item = document.createElement('div');
984
- item.className = 'jux-barchart-legend-item';
985
-
986
- const swatch = document.createElement('div');
987
- swatch.className = 'jux-barchart-legend-swatch';
988
- swatch.style.background = color;
989
-
990
- const label = document.createElement('span');
991
- label.className = 'jux-barchart-legend-label';
992
- label.textContent = point.label;
993
-
994
- item.appendChild(swatch);
995
- item.appendChild(label);
996
- legend.appendChild(item);
997
- });
998
-
999
- return legend;
1000
- }
1001
-
1002
- private _createDataTable(): HTMLElement {
1003
- const { data, xAxisLabel, yAxisLabel, chartOrientation } = this.state;
1004
-
1005
- const table = document.createElement('table');
1006
- table.className = 'jux-barchart-table';
1007
-
1008
- const thead = document.createElement('thead');
1009
- const headerRow = document.createElement('tr');
1010
-
1011
- // Swap headers based on orientation
1012
- const columnHeaders = chartOrientation === 'horizontal'
1013
- ? [yAxisLabel || 'Label', xAxisLabel || 'Value']
1014
- : [xAxisLabel || 'Label', yAxisLabel || 'Value'];
1015
-
1016
- columnHeaders.forEach(text => {
1017
- const th = document.createElement('th');
1018
- th.textContent = text;
1019
- headerRow.appendChild(th);
1020
- });
1021
- thead.appendChild(headerRow);
1022
- table.appendChild(thead);
1023
-
1024
- const tbody = document.createElement('tbody');
1025
- data.forEach(point => {
1026
- const row = document.createElement('tr');
1027
-
1028
- const labelCell = document.createElement('td');
1029
- labelCell.textContent = point.label;
1030
-
1031
- const valueCell = document.createElement('td');
1032
- valueCell.textContent = point.value.toString();
1033
-
1034
- row.appendChild(labelCell);
1035
- row.appendChild(valueCell);
1036
- tbody.appendChild(row);
1037
- });
1038
- table.appendChild(tbody);
1039
-
1040
- return table;
1041
- }
1042
-
1043
- private _lightenColor(color: string, percent: number): string {
1044
- const num = parseInt(color.replace('#', ''), 16);
1045
- const r = Math.min(255, Math.floor((num >> 16) + ((255 - (num >> 16)) * percent / 100)));
1046
- const g = Math.min(255, Math.floor(((num >> 8) & 0x00FF) + ((255 - ((num >> 8) & 0x00FF)) * percent / 100)));
1047
- const b = Math.min(255, Math.floor((num & 0x0000FF) + ((255 - (num & 0x0000FF)) * percent / 100)));
1048
- return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`;
1049
- }
1050
-
1051
- private _applyTheme(themeName: string): void {
1052
- const themes: Record<string, any> = {
1053
- google: googleTheme,
1054
- seriesa: seriesaTheme,
1055
- hr: hrTheme,
1056
- figma: figmaTheme,
1057
- notion: notionTheme,
1058
- chalk: chalkTheme,
1059
- mint: mintTheme
1060
- };
1061
-
1062
- const theme = themes[themeName];
1063
- if (!theme) return;
1064
-
1065
- // Apply colors
1066
- this.state.colors = theme.colors;
1067
-
1068
- // Inject base styles (once)
1069
- const baseStyleId = 'jux-barchart-base-styles';
1070
- if (!document.getElementById(baseStyleId)) {
1071
- const style = document.createElement('style');
1072
- style.id = baseStyleId;
1073
- style.textContent = this._getBaseStyles();
1074
- document.head.appendChild(style);
1075
- }
1076
-
1077
- // Inject font (once per theme)
1078
- if (theme.font && !document.querySelector(`link[href="${theme.font}"]`)) {
1079
- const link = document.createElement('link');
1080
- link.rel = 'stylesheet';
1081
- link.href = theme.font;
1082
- document.head.appendChild(link);
1083
- }
1084
-
1085
- // Apply theme-specific styles
1086
- const styleId = `jux-barchart-theme-${themeName}`;
1087
- let styleElement = document.getElementById(styleId) as HTMLStyleElement;
1088
-
1089
- if (!styleElement) {
1090
- styleElement = document.createElement('style');
1091
- styleElement.id = styleId;
1092
- document.head.appendChild(styleElement);
1093
- }
1094
-
1095
- // Generate CSS with theme variables
1096
- const variablesCSS = Object.entries(theme.variables)
1097
- .map(([key, value]) => ` ${key}: ${value};`)
1098
- .join('\n');
1099
-
1100
- styleElement.textContent = `
1101
- .jux-barchart.theme-${themeName} {
1102
- ${variablesCSS}
1103
- }
1104
- `;
1105
- }
1106
-
1107
- private _applyThemeToWrapper(wrapper: HTMLElement): void {
1108
- if (!this.state.theme) return;
1109
-
1110
- // Remove old theme classes
1111
- wrapper.classList.remove('theme-google', 'theme-seriesa', 'theme-hr', 'theme-figma', 'theme-notion', 'theme-chalk', 'theme-mint');
1112
-
1113
- // Add new theme class
1114
- wrapper.classList.add(`theme-${this.state.theme}`);
1115
- }
1116
-
1117
- private _getBaseStyles(): string {
1118
- return `
1119
- .jux-barchart {
1120
- font-family: var(--chart-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
1121
- display: inline-block;
1122
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
1123
- border-radius: 12px;
1124
- background: white;
1125
- padding: 24px;
1126
- }
1127
-
1128
- .jux-barchart-title {
1129
- margin: 0 0 0.5rem 0;
1130
- font-size: 1.25rem;
1131
- font-weight: 600;
1132
- font-family: inherit;
1133
- }
1134
-
1135
- .jux-barchart-subtitle {
1136
- margin: 0 0 1rem 0;
1137
- font-size: 0.875rem;
1138
- color: #6b7280;
1139
- font-family: inherit;
1140
- }
1141
-
1142
- .jux-barchart-legend {
1143
- display: flex;
1144
- flex-wrap: wrap;
1145
- gap: 1rem;
1146
- margin-top: 1rem;
1147
- justify-content: center;
1148
- }
1149
-
1150
- .jux-barchart-legend-item {
1151
- display: flex;
1152
- align-items: center;
1153
- gap: 0.5rem;
1154
- }
1155
-
1156
- .jux-barchart-legend-swatch {
1157
- width: 12px;
1158
- height: 12px;
1159
- border-radius: 2px;
1160
- }
1161
-
1162
- .jux-barchart-legend-label {
1163
- font-size: 0.875rem;
1164
- color: #374151;
1165
- font-family: inherit;
1166
- }
1167
-
1168
- .jux-barchart-table {
1169
- width: 100%;
1170
- margin-top: 1rem;
1171
- border-collapse: collapse;
1172
- font-size: 0.875rem;
1173
- font-family: inherit;
1174
- }
1175
-
1176
- .jux-barchart-table thead th {
1177
- text-align: left;
1178
- padding: 0.5rem;
1179
- border-bottom: 2px solid #e5e7eb;
1180
- font-weight: 600;
1181
- text-align: center;
1182
- }
1183
-
1184
- .jux-barchart-table tbody td {
1185
- padding: 0.5rem;
1186
- border-bottom: 1px solid #f3f4f6;
1187
- text-align: center;
1188
- }
1189
-
1190
- .jux-barchart-svg {
1191
- font-family: inherit;
1192
- }
1193
- `;
1194
- }
1195
-
1196
- render(targetId?: string | HTMLElement): this {
1197
- // Apply theme first if set
1198
- if (this.state.theme) {
1199
- this._applyTheme(this.state.theme);
1200
- }
1201
-
1202
- let container: HTMLElement;
1203
-
1204
- if (targetId) {
1205
- if (targetId instanceof HTMLElement) {
1206
- container = targetId;
1207
- } else {
1208
- const target = document.querySelector(targetId);
1209
- if (!target || !(target instanceof HTMLElement)) {
1210
- throw new Error(`BarChart: Target element "${targetId}" not found`);
1211
- }
1212
- container = target;
1213
- }
1214
- } else {
1215
- container = getOrCreateContainer(this._id);
1216
- }
1217
-
1218
- this.container = container;
1219
- const { class: className, style } = this.state;
1220
-
1221
- const wrapper = document.createElement('div');
1222
- wrapper.id = this._id;
1223
- wrapper.className = 'jux-barchart';
1224
-
1225
- // Add theme class
1226
- if (this.state.theme) {
1227
- wrapper.classList.add(`theme-${this.state.theme}`);
1228
- }
1229
-
1230
- // Add custom class
1231
- if (className) {
1232
- wrapper.classList.add(...className.split(' '));
1233
- }
1234
-
1235
- if (style) {
1236
- wrapper.setAttribute('style', style);
1237
- }
1238
-
1239
- container.appendChild(wrapper);
1240
-
1241
- // Build chart content
1242
- this._buildChart(wrapper);
1243
-
1244
- return this;
1245
- }
1246
- }
1247
-
1248
- export function barchart(id: string, options: BarChartOptions = {}): BarChart {
1249
- return new BarChart(id, options);
1250
- }