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.
- package/lib/components/alert.ts +124 -128
- package/lib/components/areachart.ts +169 -287
- package/lib/components/areachartsmooth.ts +2 -2
- package/lib/components/badge.ts +63 -72
- package/lib/components/barchart.ts +120 -48
- package/lib/components/button.ts +99 -101
- package/lib/components/card.ts +97 -121
- package/lib/components/chart-types.ts +159 -0
- package/lib/components/chart-utils.ts +160 -0
- package/lib/components/chart.ts +628 -48
- package/lib/components/checkbox.ts +137 -51
- package/lib/components/code.ts +89 -75
- package/lib/components/container.ts +1 -1
- package/lib/components/datepicker.ts +93 -78
- package/lib/components/dialog.ts +163 -130
- package/lib/components/divider.ts +111 -193
- package/lib/components/docs-data.json +711 -264
- package/lib/components/doughnutchart.ts +125 -57
- package/lib/components/dropdown.ts +172 -85
- package/lib/components/element.ts +66 -61
- package/lib/components/fileupload.ts +142 -171
- package/lib/components/heading.ts +64 -21
- package/lib/components/hero.ts +109 -34
- package/lib/components/icon.ts +247 -0
- package/lib/components/icons.ts +174 -0
- package/lib/components/include.ts +77 -2
- package/lib/components/input.ts +174 -125
- package/lib/components/list.ts +120 -79
- package/lib/components/menu.ts +97 -2
- package/lib/components/modal.ts +144 -63
- package/lib/components/nav.ts +153 -52
- package/lib/components/paragraph.ts +78 -28
- package/lib/components/progress.ts +83 -107
- package/lib/components/radio.ts +151 -52
- package/lib/components/select.ts +110 -102
- package/lib/components/sidebar.ts +148 -105
- package/lib/components/switch.ts +124 -125
- package/lib/components/table.ts +214 -137
- package/lib/components/tabs.ts +194 -113
- package/lib/components/theme-toggle.ts +38 -7
- package/lib/components/tooltip.ts +207 -47
- package/lib/jux.ts +24 -5
- package/lib/reactivity/state.ts +13 -299
- 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
|
-
|
|
360
|
+
|
|
361
|
+
// Don't auto-render JSON files
|
|
362
|
+
if (imp['type'] !== 'json') {
|
|
363
|
+
imp.render();
|
|
364
|
+
}
|
|
365
|
+
|
|
291
366
|
return imp;
|
|
292
367
|
}
|