juxscript 1.1.367 → 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/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
|
+
}
|