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