juxscript 1.0.18 → 1.0.20

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 (44) hide show
  1. package/lib/components/alert.ts +124 -128
  2. package/lib/components/areachart.ts +169 -287
  3. package/lib/components/areachartsmooth.ts +2 -2
  4. package/lib/components/badge.ts +63 -72
  5. package/lib/components/barchart.ts +120 -48
  6. package/lib/components/button.ts +99 -101
  7. package/lib/components/card.ts +97 -121
  8. package/lib/components/chart-types.ts +159 -0
  9. package/lib/components/chart-utils.ts +160 -0
  10. package/lib/components/chart.ts +628 -48
  11. package/lib/components/checkbox.ts +137 -51
  12. package/lib/components/code.ts +89 -75
  13. package/lib/components/container.ts +1 -1
  14. package/lib/components/datepicker.ts +93 -78
  15. package/lib/components/dialog.ts +163 -130
  16. package/lib/components/divider.ts +111 -193
  17. package/lib/components/docs-data.json +711 -264
  18. package/lib/components/doughnutchart.ts +125 -57
  19. package/lib/components/dropdown.ts +172 -85
  20. package/lib/components/element.ts +66 -61
  21. package/lib/components/fileupload.ts +142 -171
  22. package/lib/components/heading.ts +64 -21
  23. package/lib/components/hero.ts +109 -34
  24. package/lib/components/icon.ts +247 -0
  25. package/lib/components/icons.ts +174 -0
  26. package/lib/components/include.ts +77 -2
  27. package/lib/components/input.ts +174 -125
  28. package/lib/components/list.ts +120 -79
  29. package/lib/components/menu.ts +97 -2
  30. package/lib/components/modal.ts +144 -63
  31. package/lib/components/nav.ts +153 -52
  32. package/lib/components/paragraph.ts +78 -28
  33. package/lib/components/progress.ts +83 -107
  34. package/lib/components/radio.ts +151 -52
  35. package/lib/components/select.ts +110 -102
  36. package/lib/components/sidebar.ts +148 -105
  37. package/lib/components/switch.ts +124 -125
  38. package/lib/components/table.ts +214 -137
  39. package/lib/components/tabs.ts +194 -113
  40. package/lib/components/theme-toggle.ts +38 -7
  41. package/lib/components/tooltip.ts +207 -47
  42. package/lib/jux.ts +24 -5
  43. package/lib/reactivity/state.ts +13 -299
  44. package/package.json +1 -2
@@ -1,87 +1,64 @@
1
1
  import { getOrCreateContainer } from './helpers.js';
2
+ import { State } from '../reactivity/state.js';
2
3
 
3
- /**
4
- * Badge component options
5
- */
6
4
  export interface BadgeOptions {
7
5
  text?: string;
8
- variant?: 'default' | 'success' | 'warning' | 'error' | 'info';
9
- size?: 'sm' | 'md' | 'lg';
10
- pill?: boolean;
6
+ variant?: 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'info';
7
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
11
8
  style?: string;
12
9
  class?: string;
13
10
  }
14
11
 
15
- /**
16
- * Badge component state
17
- */
18
12
  type BadgeState = {
19
13
  text: string;
20
14
  variant: string;
21
15
  size: string;
22
- pill: boolean;
23
16
  style: string;
24
17
  class: string;
25
18
  };
26
19
 
27
- /**
28
- * Badge component - Status indicators, counts, labels
29
- *
30
- * Usage:
31
- * jux.badge('status', {
32
- * text: 'Active',
33
- * variant: 'success',
34
- * pill: true
35
- * }).render('#card');
36
- *
37
- * jux.badge('count', { text: '5' }).render('#notifications');
38
- */
39
20
  export class Badge {
40
21
  state: BadgeState;
41
22
  container: HTMLElement | null = null;
42
23
  _id: string;
43
24
  id: string;
44
25
 
26
+ private _bindings: Array<{ event: string, handler: Function }> = [];
27
+ private _syncBindings: Array<{
28
+ property: string,
29
+ stateObj: State<any>,
30
+ toState?: Function,
31
+ toComponent?: Function
32
+ }> = [];
33
+
45
34
  constructor(id: string, options: BadgeOptions = {}) {
46
35
  this._id = id;
47
36
  this.id = id;
48
37
 
49
38
  this.state = {
50
39
  text: options.text ?? '',
51
- variant: options.variant ?? 'default',
40
+ variant: options.variant ?? 'primary',
52
41
  size: options.size ?? 'md',
53
- pill: options.pill ?? false,
54
42
  style: options.style ?? '',
55
43
  class: options.class ?? ''
56
44
  };
57
45
  }
58
46
 
59
- /* -------------------------
60
- * Fluent API
61
- * ------------------------- */
62
-
63
47
  text(value: string): this {
64
48
  this.state.text = value;
65
- this._updateElement();
66
49
  return this;
67
50
  }
68
51
 
69
- variant(value: 'default' | 'success' | 'warning' | 'error' | 'info'): this {
52
+ variant(value: 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'info'): this {
70
53
  this.state.variant = value;
71
- this._updateElement();
72
54
  return this;
73
55
  }
74
56
 
75
- size(value: 'sm' | 'md' | 'lg'): this {
57
+ size(value: 'xs' | 'sm' | 'md' | 'lg' | 'xl'): this {
76
58
  this.state.size = value;
77
59
  return this;
78
60
  }
79
61
 
80
- pill(value: boolean): this {
81
- this.state.pill = value;
82
- return this;
83
- }
84
-
85
62
  style(value: string): this {
86
63
  this.state.style = value;
87
64
  return this;
@@ -92,61 +69,75 @@ export class Badge {
92
69
  return this;
93
70
  }
94
71
 
95
- /* -------------------------
96
- * Helpers
97
- * ------------------------- */
98
-
99
- private _updateElement(): void {
100
- const element = document.getElementById(this._id);
101
- if (element) {
102
- element.textContent = this.state.text;
103
- element.className = `jux-badge jux-badge-${this.state.variant} jux-badge-${this.state.size}`;
104
- if (this.state.pill) {
105
- element.classList.add('jux-badge-pill');
106
- }
107
- if (this.state.class) {
108
- element.className += ` ${this.state.class}`;
109
- }
110
- }
72
+ bind(event: string, handler: Function): this {
73
+ this._bindings.push({ event, handler });
74
+ return this;
111
75
  }
112
76
 
113
- /* -------------------------
114
- * Render
115
- * ------------------------- */
77
+ sync(property: string, stateObj: State<any>, toState?: Function, toComponent?: Function): this {
78
+ if (!stateObj || typeof stateObj.subscribe !== 'function') {
79
+ throw new Error(`Badge.sync: Expected a State object for property "${property}"`);
80
+ }
81
+ this._syncBindings.push({ property, stateObj, toState, toComponent });
82
+ return this;
83
+ }
116
84
 
117
85
  render(targetId?: string): this {
86
+ // === 1. SETUP: Get or create container ===
118
87
  let container: HTMLElement;
119
-
120
88
  if (targetId) {
121
89
  const target = document.querySelector(targetId);
122
90
  if (!target || !(target instanceof HTMLElement)) {
123
- throw new Error(`Badge: Target element "${targetId}" not found`);
91
+ throw new Error(`Badge: Target "${targetId}" not found`);
124
92
  }
125
93
  container = target;
126
94
  } else {
127
95
  container = getOrCreateContainer(this._id);
128
96
  }
129
-
130
97
  this.container = container;
131
- const { text, variant, size, pill, style, class: className } = this.state;
132
98
 
99
+ // === 2. PREPARE: Destructure state ===
100
+ const { text, variant, size, style, class: className } = this.state;
101
+
102
+ // === 3. BUILD: Create DOM elements ===
133
103
  const badge = document.createElement('span');
134
104
  badge.className = `jux-badge jux-badge-${variant} jux-badge-${size}`;
135
105
  badge.id = this._id;
136
106
  badge.textContent = text;
107
+ if (className) badge.className += ` ${className}`;
108
+ if (style) badge.setAttribute('style', style);
109
+
110
+ // === 4. WIRE: Attach event listeners and sync bindings ===
111
+
112
+ // Wire custom bindings from .bind() calls
113
+ this._bindings.forEach(({ event, handler }) => {
114
+ badge.addEventListener(event, handler as EventListener);
115
+ });
116
+
117
+ // Wire sync bindings from .sync() calls
118
+ this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
119
+ if (property === 'text') {
120
+ const transformToComponent = toComponent || ((v: any) => String(v));
121
+
122
+ stateObj.subscribe((val: any) => {
123
+ const transformed = transformToComponent(val);
124
+ badge.textContent = transformed;
125
+ this.state.text = transformed;
126
+ });
127
+ }
128
+ else if (property === 'variant') {
129
+ const transformToComponent = toComponent || ((v: any) => String(v));
130
+
131
+ stateObj.subscribe((val: any) => {
132
+ const transformed = transformToComponent(val);
133
+ badge.classList.remove(`jux-badge-${this.state.variant}`);
134
+ this.state.variant = transformed;
135
+ badge.classList.add(`jux-badge-${transformed}`);
136
+ });
137
+ }
138
+ });
137
139
 
138
- if (pill) {
139
- badge.classList.add('jux-badge-pill');
140
- }
141
-
142
- if (className) {
143
- badge.className += ` ${className}`;
144
- }
145
-
146
- if (style) {
147
- badge.setAttribute('style', style);
148
- }
149
-
140
+ // === 5. RENDER: Append to DOM and finalize ===
150
141
  container.appendChild(badge);
151
142
  return this;
152
143
  }
@@ -1,5 +1,24 @@
1
1
  import { getOrCreateContainer } from './helpers.js';
2
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';
3
22
  import {
4
23
  googleTheme,
5
24
  seriesaTheme,
@@ -384,6 +403,105 @@ export class BarChart {
384
403
  return this;
385
404
  }
386
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
+
387
505
  /* -------------------------
388
506
  * Update chart
389
507
  * ------------------------- */
@@ -1049,59 +1167,13 @@ export class BarChart {
1049
1167
  }
1050
1168
 
1051
1169
  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];
1170
+ const theme = getThemeConfig(themeName as ChartTheme);
1063
1171
  if (!theme) return;
1064
1172
 
1065
1173
  // Apply colors
1066
1174
  this.state.colors = theme.colors;
1067
1175
 
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
- `;
1176
+ applyThemeStyles(themeName as ChartTheme, 'jux-barchart', this._getBaseStyles());
1105
1177
  }
1106
1178
 
1107
1179
  private _applyThemeToWrapper(wrapper: HTMLElement): void {