juxscript 1.1.3 → 1.1.5
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/machinery/build3.js +7 -91
- package/machinery/compiler3.js +3 -131
- package/package.json +19 -5
- package/lib/components/alert.ts +0 -200
- package/lib/components/app.ts +0 -247
- package/lib/components/badge.ts +0 -101
- package/lib/components/base/BaseComponent.ts +0 -421
- package/lib/components/base/FormInput.ts +0 -227
- package/lib/components/button.ts +0 -178
- package/lib/components/card.ts +0 -173
- package/lib/components/chart.ts +0 -231
- package/lib/components/checkbox.ts +0 -242
- package/lib/components/code.ts +0 -123
- package/lib/components/container.ts +0 -140
- package/lib/components/data.ts +0 -135
- package/lib/components/datepicker.ts +0 -234
- package/lib/components/dialog.ts +0 -172
- package/lib/components/divider.ts +0 -100
- package/lib/components/dropdown.ts +0 -186
- package/lib/components/element.ts +0 -267
- package/lib/components/fileupload.ts +0 -309
- package/lib/components/grid.ts +0 -291
- package/lib/components/guard.ts +0 -92
- package/lib/components/heading.ts +0 -96
- package/lib/components/helpers.ts +0 -41
- package/lib/components/hero.ts +0 -224
- package/lib/components/icon.ts +0 -178
- package/lib/components/icons.ts +0 -464
- package/lib/components/include.ts +0 -410
- package/lib/components/input.ts +0 -457
- package/lib/components/list.ts +0 -419
- package/lib/components/loading.ts +0 -100
- package/lib/components/menu.ts +0 -275
- package/lib/components/modal.ts +0 -284
- package/lib/components/nav.ts +0 -257
- package/lib/components/paragraph.ts +0 -97
- package/lib/components/progress.ts +0 -159
- package/lib/components/radio.ts +0 -278
- package/lib/components/req.ts +0 -303
- package/lib/components/script.ts +0 -41
- package/lib/components/select.ts +0 -252
- package/lib/components/sidebar.ts +0 -275
- package/lib/components/style.ts +0 -41
- package/lib/components/switch.ts +0 -246
- package/lib/components/table.ts +0 -1249
- package/lib/components/tabs.ts +0 -250
- package/lib/components/theme-toggle.ts +0 -293
- package/lib/components/tooltip.ts +0 -144
- package/lib/components/view.ts +0 -190
- package/lib/components/write.ts +0 -272
- package/lib/layouts/default.css +0 -260
- package/lib/layouts/figma.css +0 -334
- package/lib/reactivity/state.ts +0 -78
- package/lib/utils/fetch.ts +0 -553
- package/machinery/ast.js +0 -347
- package/machinery/build.js +0 -466
- package/machinery/bundleAssets.js +0 -0
- package/machinery/bundleJux.js +0 -0
- package/machinery/bundleVendors.js +0 -0
- package/machinery/doc-generator.js +0 -136
- package/machinery/imports.js +0 -155
- package/machinery/ts-shim.js +0 -46
- package/machinery/validators/file-validator.js +0 -123
package/machinery/build3.js
CHANGED
|
@@ -18,31 +18,23 @@ try {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
// ═══════════════════════════════════════════════════════════════
|
|
21
|
-
// EXPLODE CONFIG
|
|
21
|
+
// EXPLODE CONFIG
|
|
22
22
|
// ═══════════════════════════════════════════════════════════════
|
|
23
23
|
const directories = {
|
|
24
24
|
source: rawConfig.directories?.source || './jux',
|
|
25
|
-
distribution: rawConfig.directories?.distribution || './.jux-dist'
|
|
26
|
-
themes: rawConfig.directories?.themes || './themes',
|
|
27
|
-
layouts: rawConfig.directories?.layouts || './themes/layouts',
|
|
28
|
-
assets: rawConfig.directories?.assets || './themes/assets'
|
|
25
|
+
distribution: rawConfig.directories?.distribution || './.jux-dist'
|
|
29
26
|
};
|
|
30
27
|
|
|
31
28
|
const defaults = {
|
|
32
29
|
httpPort: rawConfig.defaults?.httpPort || 3000,
|
|
33
30
|
wsPort: rawConfig.defaults?.wsPort || 3001,
|
|
34
|
-
autoRoute: rawConfig.defaults?.autoRoute ?? true
|
|
35
|
-
layout: rawConfig.defaults?.layout || null,
|
|
36
|
-
theme: rawConfig.defaults?.theme || null
|
|
31
|
+
autoRoute: rawConfig.defaults?.autoRoute ?? true
|
|
37
32
|
};
|
|
38
33
|
|
|
39
34
|
// Resolve absolute paths
|
|
40
35
|
const paths = {
|
|
41
36
|
source: path.resolve(PROJECT_ROOT, directories.source),
|
|
42
|
-
distribution: path.resolve(PROJECT_ROOT, directories.distribution)
|
|
43
|
-
themes: path.resolve(PROJECT_ROOT, directories.source, directories.themes),
|
|
44
|
-
layouts: path.resolve(PROJECT_ROOT, directories.source, directories.layouts),
|
|
45
|
-
assets: path.resolve(PROJECT_ROOT, directories.source, directories.assets)
|
|
37
|
+
distribution: path.resolve(PROJECT_ROOT, directories.distribution)
|
|
46
38
|
};
|
|
47
39
|
|
|
48
40
|
// ═══════════════════════════════════════════════════════════════
|
|
@@ -50,84 +42,10 @@ const paths = {
|
|
|
50
42
|
// ═══════════════════════════════════════════════════════════════
|
|
51
43
|
console.log(`\n📁 Directory Check:`);
|
|
52
44
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (name === 'distribution') continue;
|
|
56
|
-
|
|
57
|
-
const exists = fs.existsSync(dirPath);
|
|
58
|
-
dirStatus[name] = exists;
|
|
59
|
-
|
|
60
|
-
const icon = exists ? '✓' : '✗';
|
|
61
|
-
const suffix = exists ? '' : ' (will be skipped)';
|
|
62
|
-
console.log(` ${icon} ${name}: ${dirPath}${suffix}`);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// ═══════════════════════════════════════════════════════════════
|
|
66
|
-
// RESOLVE DEFAULT LAYOUT & THEME
|
|
67
|
-
// ═══════════════════════════════════════════════════════════════
|
|
68
|
-
function resolveFile(filename, ...searchPaths) {
|
|
69
|
-
if (!filename) return null;
|
|
70
|
-
|
|
71
|
-
// First try the filename as-is
|
|
72
|
-
if (fs.existsSync(filename)) {
|
|
73
|
-
return path.resolve(filename);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Try each search path
|
|
77
|
-
for (const searchPath of searchPaths) {
|
|
78
|
-
if (!searchPath || !fs.existsSync(searchPath)) continue;
|
|
79
|
-
|
|
80
|
-
const fullPath = path.resolve(searchPath, filename);
|
|
81
|
-
if (fs.existsSync(fullPath)) {
|
|
82
|
-
return fullPath;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
console.log(`\n🎨 Defaults Resolution:`);
|
|
90
|
-
|
|
91
|
-
// Resolve layout
|
|
92
|
-
const layoutPath = defaults.layout ? resolveFile(
|
|
93
|
-
defaults.layout,
|
|
94
|
-
paths.layouts,
|
|
95
|
-
paths.source,
|
|
96
|
-
PROJECT_ROOT
|
|
97
|
-
) : null;
|
|
98
|
-
|
|
99
|
-
if (defaults.layout) {
|
|
100
|
-
if (layoutPath) {
|
|
101
|
-
console.log(` ✓ layout: ${layoutPath}`);
|
|
102
|
-
} else {
|
|
103
|
-
console.log(` ✗ layout: "${defaults.layout}" not found, will be skipped`);
|
|
104
|
-
}
|
|
45
|
+
if (fs.existsSync(paths.source)) {
|
|
46
|
+
console.log(` ✓ source: ${paths.source}`);
|
|
105
47
|
} else {
|
|
106
|
-
console.log(`
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Resolve theme
|
|
110
|
-
const themePath = defaults.theme ? resolveFile(
|
|
111
|
-
defaults.theme,
|
|
112
|
-
paths.themes,
|
|
113
|
-
paths.source,
|
|
114
|
-
PROJECT_ROOT
|
|
115
|
-
) : null;
|
|
116
|
-
|
|
117
|
-
if (defaults.theme) {
|
|
118
|
-
if (themePath) {
|
|
119
|
-
console.log(` ✓ theme: ${themePath}`);
|
|
120
|
-
} else {
|
|
121
|
-
console.log(` ✗ theme: "${defaults.theme}" not found, will be skipped`);
|
|
122
|
-
}
|
|
123
|
-
} else {
|
|
124
|
-
console.log(` - theme: not configured`);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// ═══════════════════════════════════════════════════════════════
|
|
128
|
-
// VALIDATE SOURCE DIRECTORY EXISTS
|
|
129
|
-
// ═══════════════════════════════════════════════════════════════
|
|
130
|
-
if (!dirStatus.source) {
|
|
48
|
+
console.log(` ✗ source: ${paths.source} (not found)`);
|
|
131
49
|
console.error(`\n❌ Source directory not found: ${paths.source}`);
|
|
132
50
|
console.error(` Create the directory or update juxconfig.js`);
|
|
133
51
|
process.exit(1);
|
|
@@ -141,8 +59,6 @@ console.log(`\n`);
|
|
|
141
59
|
const compiler = new JuxCompiler({
|
|
142
60
|
srcDir: paths.source,
|
|
143
61
|
distDir: paths.distribution,
|
|
144
|
-
layoutPath,
|
|
145
|
-
themePath,
|
|
146
62
|
defaults,
|
|
147
63
|
paths
|
|
148
64
|
});
|
package/machinery/compiler3.js
CHANGED
|
@@ -13,8 +13,6 @@ export class JuxCompiler {
|
|
|
13
13
|
this.config = config;
|
|
14
14
|
this.srcDir = config.srcDir || './jux';
|
|
15
15
|
this.distDir = config.distDir || './.jux-dist';
|
|
16
|
-
this.layoutPath = config.layoutPath || null;
|
|
17
|
-
this.themePath = config.themePath || null;
|
|
18
16
|
this.defaults = config.defaults || {};
|
|
19
17
|
this.paths = config.paths || {};
|
|
20
18
|
this._juxscriptExports = null;
|
|
@@ -93,74 +91,6 @@ export class JuxCompiler {
|
|
|
93
91
|
return name.replace(/[^a-zA-Z0-9]/g, '_');
|
|
94
92
|
}
|
|
95
93
|
|
|
96
|
-
/**
|
|
97
|
-
* Copy all source assets (themes, layouts, assets folders) to dist
|
|
98
|
-
*/
|
|
99
|
-
copySourceAssets() {
|
|
100
|
-
const copied = { themes: 0, layouts: 0, assets: 0, other: 0 };
|
|
101
|
-
|
|
102
|
-
const copyDir = (srcDir, destDir, category) => {
|
|
103
|
-
if (!fs.existsSync(srcDir)) return 0;
|
|
104
|
-
|
|
105
|
-
let count = 0;
|
|
106
|
-
fs.mkdirSync(destDir, { recursive: true });
|
|
107
|
-
|
|
108
|
-
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
109
|
-
for (const entry of entries) {
|
|
110
|
-
const srcPath = path.join(srcDir, entry.name);
|
|
111
|
-
const destPath = path.join(destDir, entry.name);
|
|
112
|
-
|
|
113
|
-
if (entry.isDirectory()) {
|
|
114
|
-
count += copyDir(srcPath, destPath, category);
|
|
115
|
-
} else {
|
|
116
|
-
fs.copyFileSync(srcPath, destPath);
|
|
117
|
-
count++;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return count;
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
// Copy themes folder
|
|
124
|
-
if (this.paths.themes && fs.existsSync(this.paths.themes)) {
|
|
125
|
-
copied.themes = copyDir(this.paths.themes, path.join(this.distDir, 'themes'), 'themes');
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Copy layouts folder (if separate from themes)
|
|
129
|
-
if (this.paths.layouts && fs.existsSync(this.paths.layouts) && this.paths.layouts !== this.paths.themes) {
|
|
130
|
-
copied.layouts = copyDir(this.paths.layouts, path.join(this.distDir, 'layouts'), 'layouts');
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Copy assets folder
|
|
134
|
-
if (this.paths.assets && fs.existsSync(this.paths.assets)) {
|
|
135
|
-
copied.assets = copyDir(this.paths.assets, path.join(this.distDir, 'assets'), 'assets');
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const total = copied.themes + copied.layouts + copied.assets;
|
|
139
|
-
if (total > 0) {
|
|
140
|
-
console.log(`📂 Copied source assets: ${copied.themes} themes, ${copied.layouts} layouts, ${copied.assets} assets`);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return copied;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Generate CSS links including theme if configured
|
|
148
|
-
*/
|
|
149
|
-
generateCssLinks() {
|
|
150
|
-
const links = [];
|
|
151
|
-
|
|
152
|
-
// Note: Component CSS is no longer bundled (handled by Styler)
|
|
153
|
-
|
|
154
|
-
// User theme CSS
|
|
155
|
-
if (this.themePath) {
|
|
156
|
-
const themeName = path.basename(this.themePath);
|
|
157
|
-
links.push(` <link rel="stylesheet" href="./themes/${themeName}" id="jux-theme">`);
|
|
158
|
-
console.log(`🎨 🎨 🎨 🎨 Included theme: ${themeName} 🎨 🎨 🎨 🎨 `);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return links.join('\n');
|
|
162
|
-
}
|
|
163
|
-
|
|
164
94
|
async loadJuxscriptExports() {
|
|
165
95
|
if (this._juxscriptExports) return this._juxscriptExports;
|
|
166
96
|
|
|
@@ -223,7 +153,6 @@ export class JuxCompiler {
|
|
|
223
153
|
Identifier(node, parent) {
|
|
224
154
|
if (parent?.type === 'CallExpression' && parent.callee === node) {
|
|
225
155
|
const name = node.name;
|
|
226
|
-
// Updated: Removed strict Capitalization check to allow facades (lowercase)
|
|
227
156
|
if (!allImports.has(name) && knownComponents.includes(name)) {
|
|
228
157
|
issues.push({
|
|
229
158
|
type: 'warning',
|
|
@@ -241,16 +170,8 @@ export class JuxCompiler {
|
|
|
241
170
|
}
|
|
242
171
|
|
|
243
172
|
/**
|
|
244
|
-
*
|
|
173
|
+
* Generate entry point without layout/theme logic
|
|
245
174
|
*/
|
|
246
|
-
getLayoutFunctionName() {
|
|
247
|
-
if (!this.layoutPath || !fs.existsSync(this.layoutPath)) return null;
|
|
248
|
-
|
|
249
|
-
const content = fs.readFileSync(this.layoutPath, 'utf8');
|
|
250
|
-
const match = content.match(/export\s+function\s+(\w+)/);
|
|
251
|
-
return match ? match[1] : null;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
175
|
generateEntryPoint(views, dataModules, sharedModules) {
|
|
255
176
|
let entry = `// Auto-generated JUX entry point\n\n`;
|
|
256
177
|
const allIssues = [];
|
|
@@ -265,16 +186,6 @@ export class JuxCompiler {
|
|
|
265
186
|
}
|
|
266
187
|
});
|
|
267
188
|
|
|
268
|
-
// Check layout file for juxscript imports too
|
|
269
|
-
if (this.layoutPath && fs.existsSync(this.layoutPath)) {
|
|
270
|
-
const layoutContent = fs.readFileSync(this.layoutPath, 'utf8');
|
|
271
|
-
for (const match of layoutContent.matchAll(/import\s*\{\s*([^}]+)\s*\}\s*from\s*['"]juxscript['"]/g)) {
|
|
272
|
-
match[1].split(',').map(s => s.trim()).forEach(imp => {
|
|
273
|
-
if (imp) juxImports.add(imp);
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
189
|
if (juxImports.size > 0) {
|
|
279
190
|
entry += `import { ${[...juxImports].sort().join(', ')} } from 'juxscript';\n\n`;
|
|
280
191
|
}
|
|
@@ -294,25 +205,6 @@ export class JuxCompiler {
|
|
|
294
205
|
entry += `\nObject.assign(window, { ${[...juxImports].join(', ')} });\n`;
|
|
295
206
|
}
|
|
296
207
|
|
|
297
|
-
// Add layout function if configured
|
|
298
|
-
const layoutFnName = this.getLayoutFunctionName();
|
|
299
|
-
if (layoutFnName && this.layoutPath) {
|
|
300
|
-
const layoutContent = fs.readFileSync(this.layoutPath, 'utf8');
|
|
301
|
-
const layoutCode = this.removeImports(layoutContent);
|
|
302
|
-
entry += `\n// --- DEFAULT LAYOUT ---\n`;
|
|
303
|
-
entry += layoutCode;
|
|
304
|
-
entry += `\n`;
|
|
305
|
-
|
|
306
|
-
// Store in source snapshot
|
|
307
|
-
const layoutFile = path.basename(this.layoutPath);
|
|
308
|
-
sourceSnapshot[layoutFile] = {
|
|
309
|
-
name: 'layout',
|
|
310
|
-
file: layoutFile,
|
|
311
|
-
content: layoutContent,
|
|
312
|
-
lines: layoutContent.split('\n')
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
|
|
316
208
|
entry += `\n// --- VIEW FUNCTIONS ---\n`;
|
|
317
209
|
|
|
318
210
|
views.forEach(v => {
|
|
@@ -341,7 +233,6 @@ export class JuxCompiler {
|
|
|
341
233
|
|
|
342
234
|
this._sourceSnapshot = sourceSnapshot;
|
|
343
235
|
this._validationIssues = allIssues;
|
|
344
|
-
this._layoutFnName = layoutFnName;
|
|
345
236
|
entry += this._generateRouter(views);
|
|
346
237
|
return entry;
|
|
347
238
|
}
|
|
@@ -371,11 +262,6 @@ export class JuxCompiler {
|
|
|
371
262
|
routeMap += ` '/${v.name.toLowerCase()}': render${cap},\n`;
|
|
372
263
|
});
|
|
373
264
|
|
|
374
|
-
// Initialize layout call if configured
|
|
375
|
-
const layoutInit = this._layoutFnName
|
|
376
|
-
? `\n// Initialize default layout\nif (typeof ${this._layoutFnName} === 'function') {\n ${this._layoutFnName}();\n}\n`
|
|
377
|
-
: '';
|
|
378
|
-
|
|
379
265
|
return `
|
|
380
266
|
// --- JUX SOURCE LOADER ---
|
|
381
267
|
var __juxSources = null;
|
|
@@ -400,14 +286,6 @@ function __juxFindSource(stack) {
|
|
|
400
286
|
}
|
|
401
287
|
}
|
|
402
288
|
}
|
|
403
|
-
// Also check for layout
|
|
404
|
-
if (stack.indexOf('Layout') > -1) {
|
|
405
|
-
for (var file in __juxSources || {}) {
|
|
406
|
-
if (file.indexOf('layout') > -1) {
|
|
407
|
-
return { file: file, source: __juxSources[file] };
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
289
|
return null;
|
|
412
290
|
}
|
|
413
291
|
|
|
@@ -508,7 +386,7 @@ window.addEventListener('unhandledrejection', function(e) { __juxErrorOverlay.sh
|
|
|
508
386
|
|
|
509
387
|
// --- JUX ROUTER ---
|
|
510
388
|
const routes = {\n${routeMap}};
|
|
511
|
-
|
|
389
|
+
|
|
512
390
|
async function navigate(path) {
|
|
513
391
|
const view = routes[path];
|
|
514
392
|
if (!view) {
|
|
@@ -555,9 +433,6 @@ navigate(location.pathname);
|
|
|
555
433
|
const { views, dataModules, sharedModules } = this.scanFiles();
|
|
556
434
|
console.log(`📁 Found ${views.length} views, ${sharedModules.length} shared, ${dataModules.length} data`);
|
|
557
435
|
|
|
558
|
-
// Copy source assets (themes, layouts, assets)
|
|
559
|
-
this.copySourceAssets();
|
|
560
|
-
|
|
561
436
|
// Copy data/shared modules to dist
|
|
562
437
|
const juxDistDir = path.join(this.distDir, 'jux');
|
|
563
438
|
fs.mkdirSync(juxDistDir, { recursive: true });
|
|
@@ -602,7 +477,6 @@ navigate(location.pathname);
|
|
|
602
477
|
return { success: false, errors: [{ message: err.message }], warnings: [] };
|
|
603
478
|
}
|
|
604
479
|
|
|
605
|
-
// Generate HTML with layout container sibling to app
|
|
606
480
|
const html = `<!DOCTYPE html>
|
|
607
481
|
<html lang="en">
|
|
608
482
|
<head>
|
|
@@ -612,9 +486,7 @@ navigate(location.pathname);
|
|
|
612
486
|
<script type="module" src="./bundle.js"></script>
|
|
613
487
|
</head>
|
|
614
488
|
<body>
|
|
615
|
-
|
|
616
|
-
<div id="app"></div>
|
|
617
|
-
</div>
|
|
489
|
+
<div id="app"></div>
|
|
618
490
|
</body>
|
|
619
491
|
</html>`;
|
|
620
492
|
fs.writeFileSync(path.join(this.distDir, 'index.html'), html);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "juxscript",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A JavaScript UX authorship platform",
|
|
6
6
|
"main": "index.js",
|
|
@@ -19,14 +19,25 @@
|
|
|
19
19
|
"./package.json": "./package.json"
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
22
|
-
"bin",
|
|
23
|
-
"lib",
|
|
24
|
-
"machinery",
|
|
25
22
|
"index.js",
|
|
26
23
|
"index.d.ts",
|
|
24
|
+
"bin",
|
|
25
|
+
"create/*.jux",
|
|
26
|
+
"lib/**/*.js",
|
|
27
|
+
"lib/**/*.d.ts",
|
|
28
|
+
"machinery/build3.js",
|
|
29
|
+
"machinery/compiler3.js",
|
|
30
|
+
"machinery/config.js",
|
|
31
|
+
"machinery/serve.js",
|
|
32
|
+
"machinery/watcher.js",
|
|
33
|
+
"types",
|
|
27
34
|
"juxconfig.example.js",
|
|
28
35
|
"README.md",
|
|
29
|
-
"LICENSE"
|
|
36
|
+
"LICENSE",
|
|
37
|
+
"!lib/**/*.test.js",
|
|
38
|
+
"!lib/**/*.spec.js",
|
|
39
|
+
"!lib/previews/**",
|
|
40
|
+
"!**/*.map"
|
|
30
41
|
],
|
|
31
42
|
"bin": {
|
|
32
43
|
"jux": "./bin/cli.js"
|
|
@@ -46,6 +57,7 @@
|
|
|
46
57
|
},
|
|
47
58
|
"dependencies": {
|
|
48
59
|
"acorn": "^8.15.0",
|
|
60
|
+
"astray": "^1.1.1",
|
|
49
61
|
"axios": "^1.6.0",
|
|
50
62
|
"chart.js": "^4.5.1",
|
|
51
63
|
"esbuild": "^0.19.0",
|
|
@@ -54,10 +66,12 @@
|
|
|
54
66
|
},
|
|
55
67
|
"devDependencies": {
|
|
56
68
|
"@types/express": "^4.17.17",
|
|
69
|
+
"@types/jsdom": "^27.0.0",
|
|
57
70
|
"@types/node": "^20.0.0",
|
|
58
71
|
"@types/ws": "^8.5.5",
|
|
59
72
|
"acorn-walk": "^8.3.4",
|
|
60
73
|
"jsdom": "^27.4.0",
|
|
74
|
+
"ts-node": "^10.9.2",
|
|
61
75
|
"typescript": "^5.0.0"
|
|
62
76
|
}
|
|
63
77
|
}
|
package/lib/components/alert.ts
DELETED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import { BaseComponent } from './base/BaseComponent.js';
|
|
2
|
-
import { renderIcon } from './icons.js';
|
|
3
|
-
|
|
4
|
-
// Event definitions
|
|
5
|
-
const TRIGGER_EVENTS = [] as const;
|
|
6
|
-
const CALLBACK_EVENTS = ['dismiss'] as const;
|
|
7
|
-
|
|
8
|
-
export interface AlertOptions {
|
|
9
|
-
content?: string;
|
|
10
|
-
type?: 'info' | 'success' | 'warning' | 'error';
|
|
11
|
-
dismissible?: boolean;
|
|
12
|
-
icon?: string;
|
|
13
|
-
style?: string;
|
|
14
|
-
class?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
type AlertState = {
|
|
18
|
-
content: string;
|
|
19
|
-
type: string;
|
|
20
|
-
dismissible: boolean;
|
|
21
|
-
icon: string;
|
|
22
|
-
visible: boolean;
|
|
23
|
-
style: string;
|
|
24
|
-
class: string;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export class Alert extends BaseComponent<AlertState> {
|
|
28
|
-
private _alert: HTMLElement | null = null;
|
|
29
|
-
|
|
30
|
-
constructor(id: string, options: AlertOptions = {}) {
|
|
31
|
-
super(id, {
|
|
32
|
-
content: options.content ?? '',
|
|
33
|
-
type: options.type ?? 'info',
|
|
34
|
-
dismissible: options.dismissible ?? true,
|
|
35
|
-
icon: options.icon ?? '',
|
|
36
|
-
visible: true,
|
|
37
|
-
style: options.style ?? '',
|
|
38
|
-
class: options.class ?? ''
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
protected getTriggerEvents(): readonly string[] {
|
|
43
|
-
return TRIGGER_EVENTS;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
protected getCallbackEvents(): readonly string[] {
|
|
47
|
-
return CALLBACK_EVENTS;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/* ═════════════════════════════════════════════════════════════════
|
|
51
|
-
* FLUENT API
|
|
52
|
-
* ═════════════════════════════════════════════════════════════════ */
|
|
53
|
-
|
|
54
|
-
// ✅ Inherited from BaseComponent:
|
|
55
|
-
// - style(), class()
|
|
56
|
-
// - bind(), sync(), renderTo()
|
|
57
|
-
// - addClass(), removeClass(), toggleClass()
|
|
58
|
-
// - visible(), show(), hide(), toggleVisibility()
|
|
59
|
-
// - attr(), attrs(), removeAttr()
|
|
60
|
-
// - disabled(), enable(), disable()
|
|
61
|
-
// - loading(), focus(), blur(), remove()
|
|
62
|
-
|
|
63
|
-
content(value: string): this {
|
|
64
|
-
this.state.content = value;
|
|
65
|
-
return this;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
type(value: 'info' | 'success' | 'warning' | 'error'): this {
|
|
69
|
-
this.state.type = value;
|
|
70
|
-
return this;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
dismissible(value: boolean): this {
|
|
74
|
-
this.state.dismissible = value;
|
|
75
|
-
return this;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
icon(value: string): this {
|
|
79
|
-
this.state.icon = value;
|
|
80
|
-
return this;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/* ═════════════════════════════════════════════════════════════════
|
|
84
|
-
* RENDER
|
|
85
|
-
* ═════════════════════════════════════════════════════════════════ */
|
|
86
|
-
|
|
87
|
-
render(targetId?: string): this {
|
|
88
|
-
const container = this._setupContainer(targetId);
|
|
89
|
-
|
|
90
|
-
const { content, type, dismissible, icon, style, class: className } = this.state;
|
|
91
|
-
const hasVisibleSync = this._syncBindings.some(b => b.property === 'visible');
|
|
92
|
-
|
|
93
|
-
// Build alert element
|
|
94
|
-
const alert = document.createElement('div');
|
|
95
|
-
alert.className = `jux-alert jux-alert-${type}`;
|
|
96
|
-
alert.id = this._id;
|
|
97
|
-
if (className) alert.className += ` ${className}`;
|
|
98
|
-
if (style) alert.setAttribute('style', style);
|
|
99
|
-
|
|
100
|
-
if (icon) {
|
|
101
|
-
const iconEl = document.createElement('span');
|
|
102
|
-
iconEl.className = 'jux-alert-icon';
|
|
103
|
-
iconEl.appendChild(renderIcon(icon));
|
|
104
|
-
alert.appendChild(iconEl);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const contentEl = document.createElement('div');
|
|
108
|
-
contentEl.className = 'jux-alert-content';
|
|
109
|
-
contentEl.textContent = content;
|
|
110
|
-
alert.appendChild(contentEl);
|
|
111
|
-
|
|
112
|
-
if (dismissible) {
|
|
113
|
-
const closeBtn = document.createElement('button');
|
|
114
|
-
closeBtn.className = 'jux-alert-close';
|
|
115
|
-
closeBtn.innerHTML = '×';
|
|
116
|
-
alert.appendChild(closeBtn);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Default dismiss behavior (only if NOT using sync)
|
|
120
|
-
if (!hasVisibleSync && dismissible) {
|
|
121
|
-
const closeBtn = alert.querySelector('.jux-alert-close');
|
|
122
|
-
closeBtn?.addEventListener('click', () => {
|
|
123
|
-
// 🎯 Fire the dismiss callback event
|
|
124
|
-
this._triggerCallback('dismiss');
|
|
125
|
-
alert.remove();
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Wire events using inherited method
|
|
130
|
-
this._wireStandardEvents(alert);
|
|
131
|
-
|
|
132
|
-
// Wire sync bindings
|
|
133
|
-
this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
|
|
134
|
-
if (property === 'content') {
|
|
135
|
-
const transform = toComponent || ((v: any) => String(v));
|
|
136
|
-
|
|
137
|
-
stateObj.subscribe((val: any) => {
|
|
138
|
-
const transformed = transform(val);
|
|
139
|
-
contentEl.textContent = transformed;
|
|
140
|
-
this.state.content = transformed;
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
else if (property === 'type') {
|
|
144
|
-
const transform = toComponent || ((v: any) => String(v));
|
|
145
|
-
|
|
146
|
-
stateObj.subscribe((val: any) => {
|
|
147
|
-
const transformed = transform(val);
|
|
148
|
-
alert.className = `jux-alert jux-alert-${transformed}`;
|
|
149
|
-
if (className) alert.className += ` ${className}`;
|
|
150
|
-
this.state.type = transformed;
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
else if (property === 'visible') {
|
|
154
|
-
const transformToState = toState || ((v: any) => Boolean(v));
|
|
155
|
-
const transform = toComponent || ((v: any) => Boolean(v));
|
|
156
|
-
|
|
157
|
-
let isUpdating = false;
|
|
158
|
-
|
|
159
|
-
// State → Component
|
|
160
|
-
stateObj.subscribe((val: any) => {
|
|
161
|
-
if (isUpdating) return;
|
|
162
|
-
const transformed = transform(val);
|
|
163
|
-
alert.style.display = transformed ? 'flex' : 'none';
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
// Component → State (close button)
|
|
167
|
-
if (dismissible) {
|
|
168
|
-
const closeBtn = alert.querySelector('.jux-alert-close');
|
|
169
|
-
closeBtn?.addEventListener('click', () => {
|
|
170
|
-
if (isUpdating) return;
|
|
171
|
-
isUpdating = true;
|
|
172
|
-
|
|
173
|
-
alert.style.display = 'none';
|
|
174
|
-
stateObj.set(transformToState(false));
|
|
175
|
-
|
|
176
|
-
// 🎯 Fire the dismiss callback event
|
|
177
|
-
this._triggerCallback('dismiss');
|
|
178
|
-
|
|
179
|
-
setTimeout(() => { isUpdating = false; }, 0);
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
container.appendChild(alert);
|
|
186
|
-
this._alert = alert;
|
|
187
|
-
|
|
188
|
-
requestAnimationFrame(() => {
|
|
189
|
-
if ((window as any).lucide) {
|
|
190
|
-
(window as any).lucide.createIcons();
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
return this;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
export function alert(id: string, options: AlertOptions = {}): Alert {
|
|
199
|
-
return new Alert(id, options);
|
|
200
|
-
}
|