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
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Icon utilities for components
3
+ * Handles emoji-to-Lucide mapping, direct icon names, and image paths
4
+ */
5
+
6
+ const EMOJI_TO_LUCIDE: Record<string, string> = {
7
+ "✔️": "check-circle",
8
+ "✓": "check",
9
+ "❌": "x-circle",
10
+ "✗": "x",
11
+ "🔥": "flame",
12
+ "🚀": "rocket",
13
+ "⚙️": "settings",
14
+ "🏠": "home",
15
+ "👤": "user",
16
+ "💡": "lightbulb",
17
+ "🌈": "rainbow",
18
+ "🧪": "flask-conical",
19
+ "✉️": "mail",
20
+ "📞": "phone",
21
+ "🔍": "search",
22
+ "❤️": "heart",
23
+ "⭐": "star",
24
+ "⚠️": "alert-triangle",
25
+ "ℹ️": "info",
26
+ "❓": "help-circle",
27
+ "👁️": "eye",
28
+ "👁️‍🗨️": "eye-off",
29
+ "☰": "menu",
30
+ "🕐": "clock",
31
+ "📅": "calendar",
32
+ "⬇️": "chevron-down",
33
+ "⬆️": "chevron-up",
34
+ "⬅️": "chevron-left",
35
+ "➡️": "chevron-right",
36
+ "📈": "arrow-up",
37
+ "📉": "arrow-down",
38
+ "⬇": "download",
39
+ "📤": "upload",
40
+ "📄": "file-text",
41
+ "🗑️": "trash-2",
42
+ "✏️": "edit",
43
+ "📋": "clipboard",
44
+ "🔗": "link",
45
+ "↗️": "external-link",
46
+ "☀️": "sun",
47
+ "🌙": "moon",
48
+ "📊": "bar-chart-3",
49
+ "📁": "folder",
50
+ "💰": "coins",
51
+ "📧": "mail",
52
+ "✅": "square-check",
53
+ "🗓️": "calendar-days",
54
+ "💬": "message-circle",
55
+ "🌐": "globe",
56
+ "🔬": "microscope",
57
+ "💊": "pill",
58
+ "🔒": "lock",
59
+ "⚖️": "scale",
60
+ "🔌": "plug",
61
+ "🔐": "lock-keyhole",
62
+ "🏥": "cross",
63
+ "👥": "users",
64
+ "💚": "heart",
65
+ "💸": "banknote",
66
+ "🧾": "receipt",
67
+ "➕": "plus",
68
+ "➖": "minus",
69
+ "💾": "save"
70
+ };
71
+
72
+ const LUCIDE_CDN_URL = "https://unpkg.com/lucide@latest";
73
+
74
+ /**
75
+ * Render an icon from emoji, icon name, or image path
76
+ * @param value - Emoji (🚀), icon name (rocket), or image path (/icon.png)
77
+ * @returns HTMLElement containing the icon
78
+ *
79
+ * Usage:
80
+ * const icon = renderIcon('🚀'); // Lucide rocket icon
81
+ * const icon = renderIcon('rocket'); // Lucide rocket icon
82
+ * const icon = renderIcon('/icon.png'); // Image element
83
+ */
84
+ export function renderIcon(value: string): HTMLElement {
85
+ // Check if it's an image path (contains / or . or starts with http)
86
+ if (value.includes('/') || value.includes('.') || value.startsWith('http')) {
87
+ return createImageIcon(value);
88
+ }
89
+
90
+ // Check if it's an emoji that maps to Lucide
91
+ const lucideName = EMOJI_TO_LUCIDE[value];
92
+ if (lucideName) {
93
+ const element = createVectorIcon(lucideName);
94
+ ensureLucideLoaded();
95
+ return element;
96
+ }
97
+
98
+ // Check if it's a direct Lucide icon name (lowercase with hyphens)
99
+ if (/^[a-z][a-z0-9-]*$/.test(value)) {
100
+ const element = createVectorIcon(value);
101
+ ensureLucideLoaded();
102
+ return element;
103
+ }
104
+
105
+ // Fallback: render as emoji
106
+ return createEmojiFallback(value);
107
+ }
108
+
109
+ /**
110
+ * Render raw emoji without conversion
111
+ * @param emoji - The emoji character
112
+ * @returns HTMLElement containing just the emoji
113
+ */
114
+ export function renderEmoji(emoji: string): HTMLElement {
115
+ return createEmojiFallback(emoji);
116
+ }
117
+
118
+ /**
119
+ * Ensures Lucide is loaded and icons are rendered
120
+ */
121
+ function ensureLucideLoaded(): void {
122
+ if ((window as any).lucide) {
123
+ // Already loaded, render immediately
124
+ (window as any).lucide.createIcons();
125
+ return;
126
+ }
127
+
128
+ // Not loaded yet, inject script
129
+ if (!document.querySelector(`script[src="${LUCIDE_CDN_URL}"]`)) {
130
+ const script = document.createElement('script');
131
+ script.src = LUCIDE_CDN_URL;
132
+ script.onload = () => {
133
+ if ((window as any).lucide) {
134
+ (window as any).lucide.createIcons();
135
+ }
136
+ };
137
+ document.head.appendChild(script);
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Create Lucide icon element
143
+ */
144
+ function createVectorIcon(name: string): HTMLElement {
145
+ const iconEl = document.createElement('i');
146
+ iconEl.setAttribute('data-lucide', name);
147
+ iconEl.style.width = '24px';
148
+ iconEl.style.height = '24px';
149
+ iconEl.style.display = 'inline-block';
150
+ return iconEl;
151
+ }
152
+
153
+ /**
154
+ * Create image icon element
155
+ */
156
+ function createImageIcon(src: string): HTMLImageElement {
157
+ const img = document.createElement('img');
158
+ img.src = src;
159
+ img.style.width = '24px';
160
+ img.style.height = '24px';
161
+ img.style.display = 'inline-block';
162
+ img.style.objectFit = 'contain';
163
+ return img;
164
+ }
165
+
166
+ /**
167
+ * Render native emoji or text
168
+ */
169
+ function createEmojiFallback(emoji: string): HTMLSpanElement {
170
+ const span = document.createElement('span');
171
+ span.textContent = emoji;
172
+ span.style.display = 'inline-block';
173
+ return span;
174
+ }
@@ -5,7 +5,7 @@ import { ErrorHandler } from './error-handler.js';
5
5
  * Auto-detects resource type from URL and provides simple, fluent API
6
6
  */
7
7
 
8
- type IncludeType = 'stylesheet' | 'script' | 'image' | 'font' | 'preload' | 'prefetch' | 'module';
8
+ type IncludeType = 'stylesheet' | 'script' | 'image' | 'font' | 'preload' | 'prefetch' | 'module' | 'json';
9
9
  type IncludeLocation = 'head' | 'body-start' | 'body-end';
10
10
 
11
11
  interface IncludeOptions {
@@ -35,6 +35,7 @@ export class Include {
35
35
  private detectType(url: string): IncludeType {
36
36
  if (url.endsWith('.css')) return 'stylesheet';
37
37
  if (url.endsWith('.js') || url.endsWith('.mjs')) return 'script';
38
+ if (url.endsWith('.json')) return 'json';
38
39
  if (url.match(/\.(png|jpg|jpeg|gif|svg|webp)$/i)) return 'image';
39
40
  if (url.match(/\.(woff|woff2|ttf|otf|eot)$/i)) return 'font';
40
41
  return 'preload';
@@ -82,6 +83,65 @@ export class Include {
82
83
  return this;
83
84
  }
84
85
 
86
+ withJson(): this {
87
+ this.type = 'json';
88
+ return this;
89
+ }
90
+
91
+ /* -------------------------
92
+ * JSON Fetching
93
+ * ------------------------- */
94
+
95
+ /**
96
+ * Fetch and parse JSON file
97
+ * Returns a Promise that resolves to the parsed JSON data
98
+ *
99
+ * Usage:
100
+ * const config = await jux.include('config.json').asJson();
101
+ * const data = await jux.include('/api/data').asJson();
102
+ */
103
+ async asJson<T = any>(): Promise<T> {
104
+ try {
105
+ const response = await fetch(this.url);
106
+
107
+ if (!response.ok) {
108
+ throw new Error(`HTTP error! status: ${response.status}`);
109
+ }
110
+
111
+ const data = await response.json();
112
+ console.log(`✓ JSON loaded: ${this.url}`);
113
+ return data;
114
+ } catch (error: any) {
115
+ ErrorHandler.captureError({
116
+ component: 'Include',
117
+ method: 'asJson',
118
+ message: error.message,
119
+ stack: error.stack,
120
+ timestamp: new Date(),
121
+ context: {
122
+ url: this.url,
123
+ error: 'json_fetch_failed'
124
+ }
125
+ });
126
+ throw error;
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Fetch JSON and execute callback with data
132
+ *
133
+ * Usage:
134
+ * jux.include('config.json').onJson(data => {
135
+ * console.log('Config:', data);
136
+ * });
137
+ */
138
+ onJson<T = any>(callback: (data: T) => void): this {
139
+ this.asJson<T>().then(callback).catch(error => {
140
+ console.error('Failed to load JSON:', error);
141
+ });
142
+ return this;
143
+ }
144
+
85
145
  /* -------------------------
86
146
  * Options
87
147
  * ------------------------- */
@@ -128,6 +188,12 @@ export class Include {
128
188
  render(): this {
129
189
  if (typeof document === 'undefined') return this;
130
190
 
191
+ // Don't render JSON type (it's fetched via asJson() instead)
192
+ if (this.type === 'json') {
193
+ console.warn('Include: JSON files should be loaded with .asJson() instead of .render()');
194
+ return this;
195
+ }
196
+
131
197
  try {
132
198
  this.remove();
133
199
 
@@ -284,9 +350,18 @@ export class Include {
284
350
  * jux.include('app.mjs').withModule();
285
351
  * jux.include('custom.js').withJs({ async: true, defer: true });
286
352
  * jux.include('https://cdn.com/lib.js').inHead().defer();
353
+ *
354
+ * // For JSON:
355
+ * const data = await jux.include('config.json').asJson();
356
+ * jux.include('data.json').onJson(data => console.log(data));
287
357
  */
288
358
  export function include(urlOrFile: string): Include {
289
359
  const imp = new Include(urlOrFile);
290
- imp.render();
360
+
361
+ // Don't auto-render JSON files
362
+ if (imp['type'] !== 'json') {
363
+ imp.render();
364
+ }
365
+
291
366
  return imp;
292
367
  }