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,18 +1,17 @@
1
- import { getOrCreateContainer } from './helpers.js';
1
+ import { BaseComponent } from './base/BaseComponent.js';
2
2
  import { ErrorHandler } from './error-handler.js';
3
+ import { renderIcon } from './icons.js';
4
+
5
+ // Event definitions
6
+ const TRIGGER_EVENTS = [] as const;
7
+ const CALLBACK_EVENTS = ['themeChange'] as const;
3
8
 
4
- /**
5
- * Theme configuration
6
- */
7
9
  export interface Theme {
8
10
  id: string;
9
11
  label: string;
10
12
  icon?: string;
11
13
  }
12
14
 
13
- /**
14
- * ThemeToggle component options
15
- */
16
15
  export interface ThemeToggleOptions {
17
16
  themes?: Theme[];
18
17
  defaultTheme?: string;
@@ -23,9 +22,6 @@ export interface ThemeToggleOptions {
23
22
  class?: string;
24
23
  }
25
24
 
26
- /**
27
- * ThemeToggle component state
28
- */
29
25
  type ThemeToggleState = {
30
26
  themes: Theme[];
31
27
  currentTheme: string;
@@ -36,71 +32,48 @@ type ThemeToggleState = {
36
32
  class: string;
37
33
  };
38
34
 
39
- /**
40
- * ThemeToggle component - Manage and switch between themes
41
- *
42
- * Usage:
43
- * // Simple light/dark toggle
44
- * jux.themeToggle('myToggle').render('#appheader-actions');
45
- *
46
- * // Custom themes
47
- * jux.themeToggle('myToggle', {
48
- * themes: [
49
- * { id: 'light', label: 'Light', icon: '☀️' },
50
- * { id: 'dark', label: 'Dark', icon: '🌙' },
51
- * { id: 'auto', label: 'Auto', icon: '🌓' }
52
- * ],
53
- * variant: 'dropdown'
54
- * }).render('#appheader-actions');
55
- */
56
- export class ThemeToggle {
57
- state: ThemeToggleState;
58
- container: HTMLElement | null = null;
59
- _id: string;
60
- id: string;
61
-
35
+ export class ThemeToggle extends BaseComponent<ThemeToggleState> {
62
36
  constructor(id: string, options: ThemeToggleOptions = {}) {
63
- this._id = id;
64
- this.id = id;
65
-
66
37
  const defaultThemes: Theme[] = [
67
38
  { id: 'light', label: 'Light', icon: '☀️' },
68
39
  { id: 'dark', label: 'Dark', icon: '🌙' }
69
40
  ];
70
41
 
71
- this.state = {
42
+ super(id, {
72
43
  themes: options.themes ?? defaultThemes,
73
- currentTheme: options.defaultTheme ?? this.detectTheme(options.storageKey),
44
+ currentTheme: options.defaultTheme ?? ThemeToggle.detectTheme(options.storageKey),
74
45
  storageKey: options.storageKey ?? 'jux-theme',
75
46
  showLabel: options.showLabel ?? false,
76
47
  variant: options.variant ?? 'button',
77
48
  style: options.style ?? '',
78
49
  class: options.class ?? ''
79
- };
50
+ });
80
51
 
81
52
  // Apply theme on initialization
82
53
  this.applyTheme(this.state.currentTheme);
83
54
  }
84
55
 
85
- /* -------------------------
86
- * Theme Detection & Management
87
- * ------------------------- */
56
+ protected getTriggerEvents(): readonly string[] {
57
+ return TRIGGER_EVENTS;
58
+ }
59
+
60
+ protected getCallbackEvents(): readonly string[] {
61
+ return CALLBACK_EVENTS;
62
+ }
63
+
64
+ /* ═════════════════════════════════════════════════════════════════
65
+ * STATIC METHODS
66
+ * ═════════════════════════════════════════════════════════════════ */
88
67
 
89
- /**
90
- * Detect theme from localStorage or system preference
91
- */
92
- private detectTheme(storageKey = 'jux-theme'): string {
68
+ private static detectTheme(storageKey = 'jux-theme'): string {
93
69
  if (typeof window === 'undefined') return 'light';
94
70
 
95
- // Check localStorage first
96
71
  const stored = localStorage.getItem(storageKey);
97
72
  if (stored) return stored;
98
73
 
99
- // Check current document attribute
100
74
  const current = document.body.getAttribute('data-theme');
101
75
  if (current) return current;
102
76
 
103
- // Check system preference
104
77
  if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
105
78
  return 'dark';
106
79
  }
@@ -108,58 +81,27 @@ export class ThemeToggle {
108
81
  return 'light';
109
82
  }
110
83
 
111
- /**
112
- * Apply theme to document
113
- */
114
- private applyTheme(themeId: string): void {
115
- if (typeof document === 'undefined') return;
84
+ /* ═════════════════════════════════════════════════════════════════
85
+ * FLUENT API
86
+ * ═════════════════════════════════════════════════════════════════ */
116
87
 
117
- try {
118
- // Update data-theme attribute on body
119
- document.body.setAttribute('data-theme', themeId);
120
-
121
- // Also update on html element for broader CSS support
122
- document.documentElement.setAttribute('data-theme', themeId);
88
+ // ✅ Inherited from BaseComponent
123
89
 
124
- // Store in localStorage
125
- localStorage.setItem(this.state.storageKey, themeId);
126
-
127
- // Dispatch custom event for other components to listen
128
- window.dispatchEvent(new CustomEvent('themechange', {
129
- detail: { theme: themeId }
130
- }));
131
-
132
- console.log(`🎨 Theme applied: ${themeId}`);
133
- } catch (error: any) {
134
- ErrorHandler.captureError({
135
- component: 'ThemeToggle',
136
- method: 'applyTheme',
137
- message: `Failed to apply theme: ${error.message}`,
138
- stack: error.stack,
139
- timestamp: new Date(),
140
- context: { themeId }
141
- });
142
- }
90
+ themes(value: Theme[]): this {
91
+ this.state.themes = value;
92
+ return this;
143
93
  }
144
94
 
145
- /**
146
- * Cycle to next theme
147
- */
148
- private cycleTheme(): void {
149
- const currentIndex = this.state.themes.findIndex(t => t.id === this.state.currentTheme);
150
- const nextIndex = (currentIndex + 1) % this.state.themes.length;
151
- const nextTheme = this.state.themes[nextIndex];
152
-
153
- this.setTheme(nextTheme.id);
95
+ variant(value: 'button' | 'dropdown' | 'cycle'): this {
96
+ this.state.variant = value;
97
+ return this;
154
98
  }
155
99
 
156
- /* -------------------------
157
- * Public API
158
- * ------------------------- */
100
+ showLabel(value: boolean): this {
101
+ this.state.showLabel = value;
102
+ return this;
103
+ }
159
104
 
160
- /**
161
- * Set current theme
162
- */
163
105
  setTheme(themeId: string): this {
164
106
  const theme = this.state.themes.find(t => t.id === themeId);
165
107
 
@@ -175,16 +117,10 @@ export class ThemeToggle {
175
117
  return this;
176
118
  }
177
119
 
178
- /**
179
- * Get current theme
180
- */
181
120
  getTheme(): string {
182
121
  return this.state.currentTheme;
183
122
  }
184
123
 
185
- /**
186
- * Add a theme
187
- */
188
124
  addTheme(theme: Theme): this {
189
125
  if (!this.state.themes.find(t => t.id === theme.id)) {
190
126
  this.state.themes.push(theme);
@@ -192,38 +128,45 @@ export class ThemeToggle {
192
128
  return this;
193
129
  }
194
130
 
195
- /* -------------------------
196
- * Fluent API
197
- * ------------------------- */
131
+ /* ═════════════════════════════════════════════════════════════════
132
+ * PRIVATE METHODS
133
+ * ═════════════════════════════════════════════════════════════════ */
198
134
 
199
- themes(value: Theme[]): this {
200
- this.state.themes = value;
201
- return this;
202
- }
135
+ private applyTheme(themeId: string): void {
136
+ if (typeof document === 'undefined') return;
203
137
 
204
- variant(value: 'button' | 'dropdown' | 'cycle'): this {
205
- this.state.variant = value;
206
- return this;
207
- }
138
+ try {
139
+ document.body.setAttribute('data-theme', themeId);
140
+ document.documentElement.setAttribute('data-theme', themeId);
141
+ localStorage.setItem(this.state.storageKey, themeId);
208
142
 
209
- showLabel(value: boolean): this {
210
- this.state.showLabel = value;
211
- return this;
212
- }
143
+ window.dispatchEvent(new CustomEvent('themechange', {
144
+ detail: { theme: themeId }
145
+ }));
213
146
 
214
- style(value: string): this {
215
- this.state.style = value;
216
- return this;
217
- }
147
+ // 🎯 Fire the themeChange callback event
148
+ this._triggerCallback('themeChange', themeId);
218
149
 
219
- class(value: string): this {
220
- this.state.class = value;
221
- return this;
150
+ console.log(`🎨 Theme applied: ${themeId}`);
151
+ } catch (error: any) {
152
+ ErrorHandler.captureError({
153
+ component: 'ThemeToggle',
154
+ method: 'applyTheme',
155
+ message: `Failed to apply theme: ${error.message}`,
156
+ stack: error.stack,
157
+ timestamp: new Date(),
158
+ context: { themeId }
159
+ });
160
+ }
222
161
  }
223
162
 
224
- /* -------------------------
225
- * DOM Update
226
- * ------------------------- */
163
+ private cycleTheme(): void {
164
+ const currentIndex = this.state.themes.findIndex(t => t.id === this.state.currentTheme);
165
+ const nextIndex = (currentIndex + 1) % this.state.themes.length;
166
+ const nextTheme = this.state.themes[nextIndex];
167
+
168
+ this.setTheme(nextTheme.id);
169
+ }
227
170
 
228
171
  private _updateDOM(): void {
229
172
  if (!this.container) return;
@@ -234,17 +177,27 @@ export class ThemeToggle {
234
177
  const currentTheme = this.state.themes.find(t => t.id === this.state.currentTheme);
235
178
  if (!currentTheme) return;
236
179
 
237
- // Update button variant
238
180
  if (this.state.variant === 'button' || this.state.variant === 'cycle') {
239
181
  const button = toggle.querySelector('button');
240
- if (button) {
241
- button.innerHTML = this.state.showLabel
242
- ? `${currentTheme.icon || ''} ${currentTheme.label}`.trim()
243
- : currentTheme.icon || currentTheme.label;
182
+ if (button && currentTheme.icon) {
183
+ button.innerHTML = '';
184
+ const iconElement = renderIcon(currentTheme.icon);
185
+ button.appendChild(iconElement);
186
+
187
+ if (this.state.showLabel) {
188
+ const labelSpan = document.createElement('span');
189
+ labelSpan.textContent = ` ${currentTheme.label}`;
190
+ button.appendChild(labelSpan);
191
+ }
192
+
193
+ requestAnimationFrame(() => {
194
+ if ((window as any).lucide) {
195
+ (window as any).lucide.createIcons();
196
+ }
197
+ });
244
198
  }
245
199
  }
246
200
 
247
- // Update dropdown variant
248
201
  if (this.state.variant === 'dropdown') {
249
202
  const select = toggle.querySelector('select') as HTMLSelectElement;
250
203
  if (select) {
@@ -253,24 +206,13 @@ export class ThemeToggle {
253
206
  }
254
207
  }
255
208
 
256
- /* -------------------------
257
- * Render
258
- * ------------------------- */
209
+ /* ═════════════════════════════════════════════════════════════════
210
+ * RENDER
211
+ * ═════════════════════════════════════════════════════════════════ */
259
212
 
260
213
  render(targetId?: string): this {
261
- let container: HTMLElement;
262
-
263
- if (targetId) {
264
- const target = document.querySelector(targetId);
265
- if (!target || !(target instanceof HTMLElement)) {
266
- throw new Error(`ThemeToggle: Target element "${targetId}" not found`);
267
- }
268
- container = target;
269
- } else {
270
- container = getOrCreateContainer(this._id);
271
- }
214
+ const container = this._setupContainer(targetId);
272
215
 
273
- this.container = container;
274
216
  const { themes, currentTheme, showLabel, variant, style, class: className } = this.state;
275
217
 
276
218
  const wrapper = document.createElement('div');
@@ -287,17 +229,24 @@ export class ThemeToggle {
287
229
 
288
230
  const theme = themes.find(t => t.id === currentTheme);
289
231
 
290
- // Render based on variant
291
232
  if (variant === 'button' || variant === 'cycle') {
292
- // Single button that cycles through themes
293
233
  const button = document.createElement('button');
294
234
  button.className = 'jux-theme-toggle-button';
295
235
  button.type = 'button';
296
236
  button.setAttribute('aria-label', 'Toggle theme');
297
237
 
298
- button.innerHTML = showLabel
299
- ? `${theme?.icon || ''} ${theme?.label || currentTheme}`.trim()
300
- : theme?.icon || theme?.label || currentTheme;
238
+ if (theme?.icon) {
239
+ const iconElement = renderIcon(theme.icon);
240
+ button.appendChild(iconElement);
241
+ }
242
+
243
+ if (showLabel) {
244
+ const labelSpan = document.createElement('span');
245
+ labelSpan.textContent = ` ${theme?.label || currentTheme}`;
246
+ button.appendChild(labelSpan);
247
+ } else if (!theme?.icon) {
248
+ button.textContent = theme?.label || currentTheme;
249
+ }
301
250
 
302
251
  button.addEventListener('click', () => {
303
252
  this.cycleTheme();
@@ -306,7 +255,6 @@ export class ThemeToggle {
306
255
  wrapper.appendChild(button);
307
256
 
308
257
  } else if (variant === 'dropdown') {
309
- // Dropdown select with all themes
310
258
  const select = document.createElement('select');
311
259
  select.className = 'jux-theme-toggle-select';
312
260
  select.setAttribute('aria-label', 'Select theme');
@@ -333,29 +281,20 @@ export class ThemeToggle {
333
281
  wrapper.appendChild(select);
334
282
  }
335
283
 
336
- container.appendChild(wrapper);
337
- return this;
338
- }
284
+ this._wireStandardEvents(wrapper);
339
285
 
340
- /**
341
- * Render to another Jux component's container
342
- */
343
- renderTo(juxComponent: any): this {
344
- if (!juxComponent || typeof juxComponent !== 'object') {
345
- throw new Error('ThemeToggle.renderTo: Invalid component - not an object');
346
- }
286
+ container.appendChild(wrapper);
347
287
 
348
- if (!juxComponent._id || typeof juxComponent._id !== 'string') {
349
- throw new Error('ThemeToggle.renderTo: Invalid component - missing _id (not a Jux component)');
350
- }
288
+ requestAnimationFrame(() => {
289
+ if ((window as any).lucide) {
290
+ (window as any).lucide.createIcons();
291
+ }
292
+ });
351
293
 
352
- return this.render(`#${juxComponent._id}`);
294
+ return this;
353
295
  }
354
296
  }
355
297
 
356
- /**
357
- * Factory helper
358
- */
359
298
  export function themeToggle(id: string, options: ThemeToggleOptions = {}): ThemeToggle {
360
299
  return new ThemeToggle(id, options);
361
300
  }