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.
- package/bin/cli.js +121 -72
- package/lib/components/alert.ts +212 -165
- package/lib/components/badge.ts +93 -103
- package/lib/components/base/BaseComponent.ts +397 -0
- package/lib/components/base/FormInput.ts +322 -0
- package/lib/components/button.ts +63 -122
- package/lib/components/card.ts +109 -155
- package/lib/components/charts/areachart.ts +315 -0
- package/lib/components/charts/barchart.ts +421 -0
- package/lib/components/charts/doughnutchart.ts +263 -0
- package/lib/components/charts/lib/BaseChart.ts +402 -0
- package/lib/components/charts/lib/chart-types.ts +159 -0
- package/lib/components/charts/lib/chart-utils.ts +160 -0
- package/lib/components/charts/lib/chart.ts +707 -0
- package/lib/components/checkbox.ts +264 -127
- package/lib/components/code.ts +75 -108
- package/lib/components/container.ts +113 -130
- package/lib/components/data.ts +37 -5
- package/lib/components/datepicker.ts +195 -147
- package/lib/components/dialog.ts +187 -157
- package/lib/components/divider.ts +85 -191
- package/lib/components/docs-data.json +544 -2027
- package/lib/components/dropdown.ts +178 -136
- package/lib/components/element.ts +227 -171
- package/lib/components/fileupload.ts +285 -228
- package/lib/components/guard.ts +92 -0
- package/lib/components/heading.ts +46 -69
- package/lib/components/helpers.ts +13 -6
- package/lib/components/hero.ts +107 -95
- package/lib/components/icon.ts +160 -0
- package/lib/components/icons.ts +175 -0
- package/lib/components/include.ts +153 -5
- package/lib/components/input.ts +174 -374
- package/lib/components/kpicard.ts +16 -16
- package/lib/components/list.ts +378 -240
- package/lib/components/loading.ts +142 -211
- package/lib/components/menu.ts +103 -97
- package/lib/components/modal.ts +138 -144
- package/lib/components/nav.ts +169 -90
- package/lib/components/paragraph.ts +49 -150
- package/lib/components/progress.ts +118 -200
- package/lib/components/radio.ts +297 -149
- package/lib/components/script.ts +19 -87
- package/lib/components/select.ts +184 -186
- package/lib/components/sidebar.ts +152 -140
- package/lib/components/style.ts +19 -82
- package/lib/components/switch.ts +258 -188
- package/lib/components/table.ts +1117 -170
- package/lib/components/tabs.ts +162 -145
- package/lib/components/theme-toggle.ts +108 -169
- package/lib/components/tooltip.ts +86 -157
- package/lib/components/write.ts +108 -127
- package/lib/jux.ts +86 -41
- package/machinery/build.js +466 -0
- package/machinery/compiler.js +354 -105
- package/machinery/server.js +23 -100
- package/machinery/watcher.js +153 -130
- package/package.json +1 -2
- package/presets/base.css +1166 -0
- package/presets/notion.css +2 -1975
- package/lib/adapters/base-adapter.js +0 -35
- package/lib/adapters/index.js +0 -33
- package/lib/adapters/mysql-adapter.js +0 -65
- package/lib/adapters/postgres-adapter.js +0 -70
- package/lib/adapters/sqlite-adapter.js +0 -56
- package/lib/components/areachart.ts +0 -1246
- package/lib/components/areachartsmooth.ts +0 -1380
- package/lib/components/barchart.ts +0 -1250
- package/lib/components/chart.ts +0 -127
- package/lib/components/doughnutchart.ts +0 -1191
- package/lib/components/footer.ts +0 -165
- package/lib/components/header.ts +0 -187
- package/lib/components/layout.ts +0 -239
- package/lib/components/main.ts +0 -137
- package/lib/layouts/default.jux +0 -8
- package/lib/layouts/figma.jux +0 -0
- /package/lib/{themes → components/charts/lib}/charts.js +0 -0
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
42
|
+
super(id, {
|
|
72
43
|
themes: options.themes ?? defaultThemes,
|
|
73
|
-
currentTheme: options.defaultTheme ??
|
|
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
|
-
|
|
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
|
-
*
|
|
113
|
-
*/
|
|
114
|
-
private applyTheme(themeId: string): void {
|
|
115
|
-
if (typeof document === 'undefined') return;
|
|
84
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
85
|
+
* FLUENT API
|
|
86
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
116
87
|
|
|
117
|
-
|
|
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
|
-
|
|
125
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
197
|
-
*
|
|
131
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
132
|
+
* PRIVATE METHODS
|
|
133
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
198
134
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return this;
|
|
202
|
-
}
|
|
135
|
+
private applyTheme(themeId: string): void {
|
|
136
|
+
if (typeof document === 'undefined') return;
|
|
203
137
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
143
|
+
window.dispatchEvent(new CustomEvent('themechange', {
|
|
144
|
+
detail: { theme: themeId }
|
|
145
|
+
}));
|
|
213
146
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
return this;
|
|
217
|
-
}
|
|
147
|
+
// 🎯 Fire the themeChange callback event
|
|
148
|
+
this._triggerCallback('themeChange', themeId);
|
|
218
149
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
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 =
|
|
242
|
-
|
|
243
|
-
|
|
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
|
-
*
|
|
258
|
-
*
|
|
209
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
210
|
+
* RENDER
|
|
211
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
259
212
|
|
|
260
213
|
render(targetId?: string): this {
|
|
261
|
-
|
|
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
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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
|
-
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
288
|
+
requestAnimationFrame(() => {
|
|
289
|
+
if ((window as any).lucide) {
|
|
290
|
+
(window as any).lucide.createIcons();
|
|
291
|
+
}
|
|
292
|
+
});
|
|
351
293
|
|
|
352
|
-
return this
|
|
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
|
}
|