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.367",
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,7 @@
1
+ import { jux } from 'juxscript';
2
+ import { juxSidebar } from './sidebar.jux';
3
+ const allRoutes = jux.routes.all();
4
+
5
+ juxSidebar('my-sidebar', allRoutes,
6
+ { title: 'Simple App', logo: 'J', footer: 'v1.0', density: 'inflated', collapsible: true });
7
+
@@ -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
+ }