juxscript 1.1.366 → 1.1.368
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
CHANGED
|
@@ -4,6 +4,7 @@ import path from 'path';
|
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import { spawn } from 'child_process';
|
|
7
|
+
import { createInterface } from 'readline';
|
|
7
8
|
|
|
8
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
10
|
const __dirname = path.dirname(__filename);
|
|
@@ -287,23 +288,154 @@ Usage:
|
|
|
287
288
|
jux build Build for production
|
|
288
289
|
jux serve Start production server
|
|
289
290
|
jux serve --hot Start dev server with hot reload
|
|
291
|
+
jux comp [name] Add a component preset to your project
|
|
290
292
|
|
|
291
293
|
Options:
|
|
292
294
|
--help, -h Show this help message
|
|
293
295
|
`);
|
|
294
296
|
}
|
|
295
297
|
|
|
298
|
+
// ═══════════════════════════════════════════════════════════════
|
|
299
|
+
// COMMAND: comp [name]
|
|
300
|
+
// Copies a preset component into the project's jux directory
|
|
301
|
+
// ═══════════════════════════════════════════════════════════════
|
|
302
|
+
async function runComp(compName) {
|
|
303
|
+
const presetsDir = path.join(PACKAGE_ROOT, 'presets');
|
|
304
|
+
|
|
305
|
+
if (!fs.existsSync(presetsDir)) {
|
|
306
|
+
console.error('❌ No presets directory found in juxscript package.');
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Discover available presets (each subfolder is a preset)
|
|
311
|
+
const available = fs.readdirSync(presetsDir, { withFileTypes: true })
|
|
312
|
+
.filter(d => d.isDirectory())
|
|
313
|
+
.map(d => d.name);
|
|
314
|
+
|
|
315
|
+
if (available.length === 0) {
|
|
316
|
+
console.error('❌ No presets available.');
|
|
317
|
+
process.exit(1);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// If no name given, show interactive list
|
|
321
|
+
if (!compName) {
|
|
322
|
+
compName = await promptPresetSelection(available);
|
|
323
|
+
if (!compName) {
|
|
324
|
+
console.log('Cancelled.');
|
|
325
|
+
process.exit(0);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Validate preset exists
|
|
330
|
+
const presetDir = path.join(presetsDir, compName);
|
|
331
|
+
if (!fs.existsSync(presetDir)) {
|
|
332
|
+
console.error(`❌ Preset "${compName}" not found.`);
|
|
333
|
+
console.log(`\nAvailable presets:`);
|
|
334
|
+
available.forEach(p => console.log(` • ${p}`));
|
|
335
|
+
process.exit(1);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Resolve target directory from juxconfig.js or default 'jux'
|
|
339
|
+
const targetSrcDir = resolveProjectSrcDir();
|
|
340
|
+
|
|
341
|
+
if (!fs.existsSync(targetSrcDir)) {
|
|
342
|
+
fs.mkdirSync(targetSrcDir, { recursive: true });
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Copy all files from preset to project src dir
|
|
346
|
+
const presetFiles = fs.readdirSync(presetDir, { withFileTypes: true });
|
|
347
|
+
let copied = 0;
|
|
348
|
+
|
|
349
|
+
for (const entry of presetFiles) {
|
|
350
|
+
if (entry.isFile()) {
|
|
351
|
+
const srcPath = path.join(presetDir, entry.name);
|
|
352
|
+
const destPath = path.join(targetSrcDir, entry.name);
|
|
353
|
+
|
|
354
|
+
if (fs.existsSync(destPath)) {
|
|
355
|
+
const overwrite = await promptYesNo(` ⚠️ "${entry.name}" already exists. Overwrite?`);
|
|
356
|
+
if (!overwrite) {
|
|
357
|
+
console.log(` ⏭️ Skipped ${entry.name}`);
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
fs.copyFileSync(srcPath, destPath);
|
|
363
|
+
console.log(` ✅ ${entry.name}`);
|
|
364
|
+
copied++;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
console.log(`\n✅ Copied ${copied} file(s) from preset "${compName}" to ${path.relative(process.cwd(), targetSrcDir)}/\n`);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function resolveProjectSrcDir() {
|
|
372
|
+
const projectRoot = process.cwd();
|
|
373
|
+
const configPath = path.join(projectRoot, 'juxconfig.js');
|
|
374
|
+
|
|
375
|
+
if (fs.existsSync(configPath)) {
|
|
376
|
+
try {
|
|
377
|
+
const configContent = fs.readFileSync(configPath, 'utf8');
|
|
378
|
+
const srcDirMatch = configContent.match(/srcDir\s*:\s*['"]([^'"]+)['"]/);
|
|
379
|
+
if (srcDirMatch) {
|
|
380
|
+
return path.resolve(projectRoot, srcDirMatch[1]);
|
|
381
|
+
}
|
|
382
|
+
} catch (_) { }
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return path.resolve(projectRoot, 'jux');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function promptPresetSelection(presets) {
|
|
389
|
+
return new Promise((resolve) => {
|
|
390
|
+
console.log('\n📦 Available component presets:\n');
|
|
391
|
+
presets.forEach((p, i) => {
|
|
392
|
+
// Read files in preset to show what's included
|
|
393
|
+
const presetDir = path.join(PACKAGE_ROOT, 'presets', p);
|
|
394
|
+
const files = fs.readdirSync(presetDir).filter(f => !f.startsWith('.'));
|
|
395
|
+
console.log(` ${i + 1}) ${p} (${files.join(', ')})`);
|
|
396
|
+
});
|
|
397
|
+
console.log(` 0) Cancel\n`);
|
|
398
|
+
|
|
399
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
400
|
+
rl.question('Select a preset (number or name): ', (answer) => {
|
|
401
|
+
rl.close();
|
|
402
|
+
const trimmed = answer.trim();
|
|
403
|
+
|
|
404
|
+
// By number
|
|
405
|
+
const num = parseInt(trimmed, 10);
|
|
406
|
+
if (num === 0) return resolve(null);
|
|
407
|
+
if (num >= 1 && num <= presets.length) return resolve(presets[num - 1]);
|
|
408
|
+
|
|
409
|
+
// By name
|
|
410
|
+
if (presets.includes(trimmed)) return resolve(trimmed);
|
|
411
|
+
|
|
412
|
+
console.error(`❌ Invalid selection: "${trimmed}"`);
|
|
413
|
+
resolve(null);
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function promptYesNo(question) {
|
|
419
|
+
return new Promise((resolve) => {
|
|
420
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
421
|
+
rl.question(`${question} (y/N): `, (answer) => {
|
|
422
|
+
rl.close();
|
|
423
|
+
resolve(answer.trim().toLowerCase() === 'y');
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
|
|
296
428
|
// ═══════════════════════════════════════════════════════════════
|
|
297
429
|
// MAIN ROUTER
|
|
298
430
|
// ═══════════════════════════════════════════════════════════════
|
|
299
431
|
switch (command) {
|
|
300
432
|
case 'create':
|
|
301
|
-
createProject(args[0]);
|
|
433
|
+
createProject(args[0]);
|
|
302
434
|
break;
|
|
303
435
|
|
|
304
436
|
case 'init':
|
|
305
437
|
case 'install':
|
|
306
|
-
initProject();
|
|
438
|
+
initProject();
|
|
307
439
|
break;
|
|
308
440
|
|
|
309
441
|
case 'build':
|
|
@@ -315,6 +447,12 @@ switch (command) {
|
|
|
315
447
|
runServe();
|
|
316
448
|
break;
|
|
317
449
|
|
|
450
|
+
case 'comp':
|
|
451
|
+
case 'component':
|
|
452
|
+
case 'preset':
|
|
453
|
+
runComp(args[0]);
|
|
454
|
+
break;
|
|
455
|
+
|
|
318
456
|
case '--help':
|
|
319
457
|
case '-h':
|
|
320
458
|
case undefined:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "juxscript",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.368",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A JavaScript UX authorship platform",
|
|
6
6
|
"main": "./dist/lib/index.js",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"dom-structure-map.json",
|
|
25
25
|
"bin",
|
|
26
26
|
"create",
|
|
27
|
+
"presets",
|
|
27
28
|
"machinery",
|
|
28
29
|
"juxconfig.example.js",
|
|
29
30
|
"README.md",
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { jux, pageState } from 'juxscript';
|
|
2
|
+
|
|
3
|
+
var COLLAPSE_ICON_OPEN = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2"></rect><path d="M9 3v18"></path></svg>';
|
|
4
|
+
var COLLAPSE_ICON_CLOSED = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="transform:scaleX(-1)"><rect width="18" height="18" x="3" y="3" rx="2"></rect><path d="M9 3v18"></path></svg>';
|
|
5
|
+
|
|
6
|
+
export function juxSidebar(id, sections, options) {
|
|
7
|
+
options = options || {};
|
|
8
|
+
var _title = options.title || 'App';
|
|
9
|
+
var _logo = options.logo || _title.charAt(0).toUpperCase();
|
|
10
|
+
var _footer = options.footer || '';
|
|
11
|
+
var _width = options.width || '260px';
|
|
12
|
+
var _collapsedWidth = options.collapsedWidth || '52px';
|
|
13
|
+
var _target = options.target;
|
|
14
|
+
var _density = options.density || 'normal';
|
|
15
|
+
var _collapsible = options.collapsible !== undefined ? options.collapsible : false;
|
|
16
|
+
var _collapsed = options.collapsed || false;
|
|
17
|
+
var _sectionCount = 0;
|
|
18
|
+
var _rendered = false;
|
|
19
|
+
var _footerRendered = false;
|
|
20
|
+
|
|
21
|
+
var DENSITY = {
|
|
22
|
+
dense: { headerPad: '10px', itemPad: '4px 10px', labelPad: '6px 10px 2px', footerPad: '8px 12px', gap: '0px', fontSize: '12px', iconSize: '13px' },
|
|
23
|
+
normal: { headerPad: '16px', itemPad: '8px 12px', labelPad: '8px 12px 4px', footerPad: '12px 16px', gap: '1px', fontSize: '13px', iconSize: '15px' },
|
|
24
|
+
inflated: { headerPad: '20px', itemPad: '12px 16px', labelPad: '12px 16px 6px', footerPad: '16px 20px', gap: '4px', fontSize: '14px', iconSize: '17px' }
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function normalizeItem(item) {
|
|
28
|
+
if (typeof item === 'string') {
|
|
29
|
+
var path = item.startsWith('/') ? item : '/' + item;
|
|
30
|
+
var seg = path.split('/').filter(Boolean).pop() || 'Home';
|
|
31
|
+
return { id: id + '-' + seg.toLowerCase().replace(/[^a-z0-9]/g, '-'), label: seg.charAt(0).toUpperCase() + seg.slice(1), path: path };
|
|
32
|
+
}
|
|
33
|
+
// Support RouteInfo shape { path, name, file } from jux.routes.all()
|
|
34
|
+
var itemPath = item.path || '';
|
|
35
|
+
var seg = itemPath.split('/').filter(Boolean).pop() || 'home';
|
|
36
|
+
return {
|
|
37
|
+
id: item.id || id + '-' + seg.toLowerCase().replace(/[^a-z0-9]/g, '-'),
|
|
38
|
+
label: item.label || item.name || seg.charAt(0).toUpperCase() + seg.slice(1),
|
|
39
|
+
path: itemPath,
|
|
40
|
+
icon: item.icon
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function normalizeSections(input) {
|
|
45
|
+
if (!input || !Array.isArray(input) || input.length === 0) return [];
|
|
46
|
+
var isFlat = input.every(function (i) { return typeof i === 'string' || (typeof i === 'object' && !i.items); });
|
|
47
|
+
if (isFlat) return [{ items: input.map(normalizeItem) }];
|
|
48
|
+
return input.map(function (s) {
|
|
49
|
+
if (typeof s === 'string') return { items: [normalizeItem(s)] };
|
|
50
|
+
return { label: s.label, items: (s.items || []).map(normalizeItem) };
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function ensureShell() {
|
|
55
|
+
if (_rendered) return;
|
|
56
|
+
_rendered = true;
|
|
57
|
+
injectStyles();
|
|
58
|
+
var sidebarClass = 'jux-sidebar jux-sidebar--' + _density;
|
|
59
|
+
if (_collapsed) sidebarClass += ' jux-sidebar--collapsed';
|
|
60
|
+
jux.div(id, { class: sidebarClass, style: 'width:' + (_collapsed ? _collapsedWidth : _width), target: _target });
|
|
61
|
+
jux.div(id + '-header', { class: 'jux-sidebar-header', target: id });
|
|
62
|
+
jux.div(id + '-logo', { class: 'jux-sidebar-logo', target: id + '-header' });
|
|
63
|
+
renderLogo();
|
|
64
|
+
jux.span(id + '-title', { class: 'jux-sidebar-title-text', content: _title, target: id + '-header' });
|
|
65
|
+
if (_collapsible) {
|
|
66
|
+
jux.div(id + '-collapse-btn', {
|
|
67
|
+
class: 'jux-sidebar-collapse-btn',
|
|
68
|
+
target: id + '-header'
|
|
69
|
+
});
|
|
70
|
+
var btnEl = document.getElementById(id + '-collapse-btn');
|
|
71
|
+
if (btnEl) btnEl.innerHTML = _collapsed ? COLLAPSE_ICON_CLOSED : COLLAPSE_ICON_OPEN;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function renderLogo() {
|
|
76
|
+
var logoEl = document.getElementById(id + '-logo');
|
|
77
|
+
if (!logoEl) return;
|
|
78
|
+
if (_logo.startsWith('<svg') || _logo.startsWith('<SVG')) {
|
|
79
|
+
logoEl.innerHTML = _logo;
|
|
80
|
+
} else if (_logo.startsWith('/') || _logo.startsWith('http') || _logo.startsWith('data:')) {
|
|
81
|
+
logoEl.innerHTML = '<img src="' + _logo + '" alt="logo" class="jux-sidebar-logo-img" />';
|
|
82
|
+
} else {
|
|
83
|
+
logoEl.textContent = _logo;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function renderSection(section) {
|
|
88
|
+
ensureShell();
|
|
89
|
+
var i = _sectionCount++;
|
|
90
|
+
if (i > 0) jux.div(id + '-divider-' + i, { class: 'jux-sidebar-divider', target: id });
|
|
91
|
+
if (section.label) jux.div(id + '-label-' + i, { class: 'jux-sidebar-label', content: section.label, target: id });
|
|
92
|
+
jux.nav(id + '-nav-' + i, { target: id, items: section.items });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function renderFooter() {
|
|
96
|
+
if (_footerRendered || !_footer) return;
|
|
97
|
+
_footerRendered = true;
|
|
98
|
+
jux.div(id + '-footer', { class: 'jux-sidebar-footer', content: _footer, target: id });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function applyCollapsed(collapsed) {
|
|
102
|
+
_collapsed = collapsed;
|
|
103
|
+
var el = document.getElementById(id);
|
|
104
|
+
if (!el) return;
|
|
105
|
+
if (_collapsed) {
|
|
106
|
+
el.classList.add('jux-sidebar--collapsed');
|
|
107
|
+
el.style.width = _collapsedWidth;
|
|
108
|
+
} else {
|
|
109
|
+
el.classList.remove('jux-sidebar--collapsed');
|
|
110
|
+
el.style.width = _width;
|
|
111
|
+
}
|
|
112
|
+
if (_collapsible) {
|
|
113
|
+
var btnEl = document.getElementById(id + '-collapse-btn');
|
|
114
|
+
if (btnEl) btnEl.innerHTML = _collapsed ? COLLAPSE_ICON_CLOSED : COLLAPSE_ICON_OPEN;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function injectStyles() {
|
|
119
|
+
jux.style('menu-styles', '\
|
|
120
|
+
body { margin: 0; }\
|
|
121
|
+
.jux-sidebar { display:flex; flex-direction:column; min-height:100vh; background:hsl(var(--card)); color:hsl(var(--card-foreground)); border-right:1px solid hsl(var(--border)); font-family:var(--font-sans,system-ui,sans-serif); transition:width 0.2s ease; overflow:hidden; }\
|
|
122
|
+
.jux-sidebar-header { display:flex; align-items:center; gap:8px; font-size:14px; font-weight:600; border-bottom:1px solid hsl(var(--border)); min-height:20px; position:relative; }\
|
|
123
|
+
.jux-sidebar-logo { width:20px; height:20px; min-width:20px; border-radius:6px; background:hsl(var(--primary)); display:flex; align-items:center; justify-content:center; color:hsl(var(--primary-foreground)); font-size:11px; font-weight:700; flex-shrink:0; overflow:hidden; }\
|
|
124
|
+
.jux-sidebar-logo-img { width:100%; height:100%; object-fit:cover; border-radius:inherit; }\
|
|
125
|
+
.jux-sidebar-logo svg { width:14px; height:14px; }\
|
|
126
|
+
.jux-sidebar-title-text { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; transition:opacity 0.2s ease, max-width 0.2s ease; max-width:200px; flex:1; }\
|
|
127
|
+
.jux-sidebar-label { font-size:11px; font-weight:500; color:hsl(var(--muted-foreground)); text-transform:uppercase; letter-spacing:0.05em; white-space:nowrap; overflow:hidden; transition:opacity 0.2s ease, max-height 0.2s ease; max-height:30px; }\
|
|
128
|
+
.jux-sidebar-footer { margin-top:auto; border-top:1px solid hsl(var(--border)); font-size:11px; color:hsl(var(--muted-foreground)); white-space:nowrap; overflow:hidden; transition:opacity 0.2s ease; }\
|
|
129
|
+
.jux-sidebar-divider { height:1px; background:hsl(var(--border)); margin:4px 8px; }\
|
|
130
|
+
.jux-nav-item { display:flex; align-items:center; border-radius:var(--radius,6px); font-weight:500; color:hsl(var(--foreground)); cursor:pointer; transition:background 0.15s; user-select:none; text-decoration:none; white-space:nowrap; overflow:hidden; }\
|
|
131
|
+
.jux-nav-item:hover { background:hsl(var(--accent)); color:hsl(var(--accent-foreground)); }\
|
|
132
|
+
.jux-nav-item--active { background:hsl(var(--accent)); color:hsl(var(--accent-foreground)); font-weight:600; }\
|
|
133
|
+
.jux-nav-item-icon { text-align:center; flex-shrink:0; }\
|
|
134
|
+
.jux-nav-item-text { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; transition:opacity 0.2s ease; }\
|
|
135
|
+
\
|
|
136
|
+
.jux-sidebar-collapse-btn { width:28px; height:28px; display:flex; align-items:center; justify-content:center; cursor:pointer; border-radius:6px; color:hsl(var(--muted-foreground)); flex-shrink:0; transition:background 0.15s, color 0.15s; margin-left:auto; }\
|
|
137
|
+
.jux-sidebar-collapse-btn:hover { background:hsl(var(--accent)); color:hsl(var(--accent-foreground)); }\
|
|
138
|
+
.jux-sidebar-collapse-btn svg { display:block; }\
|
|
139
|
+
\
|
|
140
|
+
.jux-sidebar--collapsed .jux-sidebar-header { flex-direction:column; align-items:center; gap:4px; padding-left:4px!important; padding-right:4px!important; }\
|
|
141
|
+
.jux-sidebar--collapsed .jux-sidebar-title-text { opacity:0; max-width:0; height:0; overflow:hidden; margin:0; }\
|
|
142
|
+
.jux-sidebar--collapsed .jux-sidebar-label { opacity:0; max-height:0; padding-top:0!important; padding-bottom:0!important; margin:0; }\
|
|
143
|
+
.jux-sidebar--collapsed .jux-sidebar-footer { opacity:0; height:0; padding:0!important; border:none; }\
|
|
144
|
+
.jux-sidebar--collapsed .jux-nav-item-text { opacity:0; width:0; overflow:hidden; }\
|
|
145
|
+
.jux-sidebar--collapsed .jux-nav-item { justify-content:center; gap:0!important; }\
|
|
146
|
+
.jux-sidebar--collapsed .jux-sidebar-collapse-btn { margin-left:0; width:24px; height:24px; }\
|
|
147
|
+
.jux-sidebar--collapsed .jux-sidebar-collapse-btn svg { width:14px; height:14px; }\
|
|
148
|
+
.jux-sidebar--collapsed .jux-sidebar-logo { margin:0; }\
|
|
149
|
+
\
|
|
150
|
+
.jux-sidebar--dense .jux-sidebar-header { padding:' + DENSITY.dense.headerPad + '; }\
|
|
151
|
+
.jux-sidebar--dense .jux-sidebar-label { padding:' + DENSITY.dense.labelPad + '; }\
|
|
152
|
+
.jux-sidebar--dense .jux-sidebar-footer { padding:' + DENSITY.dense.footerPad + '; }\
|
|
153
|
+
.jux-sidebar--dense .jux-nav-item { padding:' + DENSITY.dense.itemPad + '; margin:' + DENSITY.dense.gap + ' 0; font-size:' + DENSITY.dense.fontSize + '; gap:8px; }\
|
|
154
|
+
.jux-sidebar--dense .jux-nav-item-icon { width:16px; font-size:' + DENSITY.dense.iconSize + '; }\
|
|
155
|
+
\
|
|
156
|
+
.jux-sidebar--normal .jux-sidebar-header { padding:' + DENSITY.normal.headerPad + '; }\
|
|
157
|
+
.jux-sidebar--normal .jux-sidebar-label { padding:' + DENSITY.normal.labelPad + '; }\
|
|
158
|
+
.jux-sidebar--normal .jux-sidebar-footer { padding:' + DENSITY.normal.footerPad + '; }\
|
|
159
|
+
.jux-sidebar--normal .jux-nav-item { padding:' + DENSITY.normal.itemPad + '; margin:' + DENSITY.normal.gap + ' 0; font-size:' + DENSITY.normal.fontSize + '; gap:10px; }\
|
|
160
|
+
.jux-sidebar--normal .jux-nav-item-icon { width:18px; font-size:' + DENSITY.normal.iconSize + '; }\
|
|
161
|
+
\
|
|
162
|
+
.jux-sidebar--inflated .jux-sidebar-header { padding:' + DENSITY.inflated.headerPad + '; }\
|
|
163
|
+
.jux-sidebar--inflated .jux-sidebar-label { padding:' + DENSITY.inflated.labelPad + '; }\
|
|
164
|
+
.jux-sidebar--inflated .jux-sidebar-footer { padding:' + DENSITY.inflated.footerPad + '; }\
|
|
165
|
+
.jux-sidebar--inflated .jux-nav-item { padding:' + DENSITY.inflated.itemPad + '; margin:' + DENSITY.inflated.gap + ' 0; font-size:' + DENSITY.inflated.fontSize + '; gap:12px; }\
|
|
166
|
+
.jux-sidebar--inflated .jux-nav-item-icon { width:20px; font-size:' + DENSITY.inflated.iconSize + '; }\
|
|
167
|
+
');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
var api = {
|
|
171
|
+
id: id,
|
|
172
|
+
title: function (val) { _title = val; return api; },
|
|
173
|
+
logo: function (val) { _logo = val; return api; },
|
|
174
|
+
footer: function (val) {
|
|
175
|
+
_footer = val;
|
|
176
|
+
if (_rendered) renderFooter();
|
|
177
|
+
return api;
|
|
178
|
+
},
|
|
179
|
+
width: function (val) { _width = val; return api; },
|
|
180
|
+
collapsedWidth: function (val) { _collapsedWidth = val; return api; },
|
|
181
|
+
target: function (val) { _target = val; return api; },
|
|
182
|
+
density: function (val) { _density = val; return api; },
|
|
183
|
+
collapsible: function (val) { _collapsible = val !== undefined ? val : true; return api; },
|
|
184
|
+
|
|
185
|
+
section: function (label, items) {
|
|
186
|
+
renderSection({ label: label, items: (items || []).map(normalizeItem) });
|
|
187
|
+
return api;
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
items: function (items) {
|
|
191
|
+
renderSection({ items: (items || []).map(normalizeItem) });
|
|
192
|
+
return api;
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
render: function () {
|
|
196
|
+
ensureShell();
|
|
197
|
+
renderFooter();
|
|
198
|
+
if (_collapsible) {
|
|
199
|
+
pageState.__watch(function () {
|
|
200
|
+
if (pageState[id + '-collapse-btn'].click) {
|
|
201
|
+
api.toggleCollapse();
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
return api;
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
addSection: function (label, items) { return api.section(label, items); },
|
|
209
|
+
setTitle: function (val) { pageState[id + '-title'].content = val; return api; },
|
|
210
|
+
setFooter: function (val) { pageState[id + '-footer'].content = val; return api; },
|
|
211
|
+
show: function () { pageState[id].visible = true; return api; },
|
|
212
|
+
hide: function () { pageState[id].visible = false; return api; },
|
|
213
|
+
toggle: function () { pageState[id].visible = !pageState[id].visible; return api; },
|
|
214
|
+
collapse: function () { applyCollapsed(true); return api; },
|
|
215
|
+
expand: function () { applyCollapsed(false); return api; },
|
|
216
|
+
toggleCollapse: function () { applyCollapsed(!_collapsed); return api; },
|
|
217
|
+
isCollapsed: function () { return _collapsed; },
|
|
218
|
+
getElement: function () { return document.getElementById(id); }
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
if (sections && Array.isArray(sections) && sections.length > 0) {
|
|
222
|
+
var normalized = normalizeSections(sections);
|
|
223
|
+
normalized.forEach(renderSection);
|
|
224
|
+
renderFooter();
|
|
225
|
+
if (_collapsible) {
|
|
226
|
+
pageState.__watch(function () {
|
|
227
|
+
if (pageState[id + '-collapse-btn'].click) {
|
|
228
|
+
api.toggleCollapse();
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return api;
|
|
235
|
+
}
|