juxscript 1.1.13 → 1.1.15

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.
Files changed (124) hide show
  1. package/index.d.ts +0 -2
  2. package/index.d.ts.map +1 -1
  3. package/index.js +0 -2
  4. package/lib/components/alert.d.ts +4 -0
  5. package/lib/components/alert.d.ts.map +1 -1
  6. package/lib/components/alert.js +19 -0
  7. package/lib/components/alert.ts +19 -0
  8. package/lib/components/badge.d.ts +3 -4
  9. package/lib/components/badge.d.ts.map +1 -1
  10. package/lib/components/badge.js +20 -0
  11. package/lib/components/badge.ts +20 -4
  12. package/lib/components/base/BaseComponent.d.ts +35 -6
  13. package/lib/components/base/BaseComponent.d.ts.map +1 -1
  14. package/lib/components/base/BaseComponent.js +20 -49
  15. package/lib/components/base/BaseComponent.ts +59 -48
  16. package/lib/components/base/FormInput.d.ts +4 -0
  17. package/lib/components/base/FormInput.d.ts.map +1 -1
  18. package/lib/components/base/FormInput.js +6 -0
  19. package/lib/components/base/FormInput.ts +7 -0
  20. package/lib/components/button.d.ts +1 -0
  21. package/lib/components/button.d.ts.map +1 -1
  22. package/lib/components/button.js +3 -0
  23. package/lib/components/button.ts +4 -0
  24. package/lib/components/card.d.ts +1 -0
  25. package/lib/components/card.d.ts.map +1 -1
  26. package/lib/components/card.js +3 -0
  27. package/lib/components/card.ts +4 -0
  28. package/lib/components/chart.d.ts +1 -0
  29. package/lib/components/chart.d.ts.map +1 -1
  30. package/lib/components/chart.js +3 -0
  31. package/lib/components/chart.ts +4 -0
  32. package/lib/components/code.d.ts +1 -0
  33. package/lib/components/code.d.ts.map +1 -1
  34. package/lib/components/code.js +3 -0
  35. package/lib/components/code.ts +4 -0
  36. package/lib/components/container.d.ts +1 -0
  37. package/lib/components/container.d.ts.map +1 -1
  38. package/lib/components/container.js +3 -0
  39. package/lib/components/container.ts +4 -0
  40. package/lib/components/dialog.d.ts +1 -0
  41. package/lib/components/dialog.d.ts.map +1 -1
  42. package/lib/components/dialog.js +3 -0
  43. package/lib/components/dialog.ts +4 -0
  44. package/lib/components/divider.d.ts +1 -0
  45. package/lib/components/divider.d.ts.map +1 -1
  46. package/lib/components/divider.js +3 -0
  47. package/lib/components/divider.ts +4 -0
  48. package/lib/components/dropdown.d.ts +1 -0
  49. package/lib/components/dropdown.d.ts.map +1 -1
  50. package/lib/components/dropdown.js +3 -0
  51. package/lib/components/dropdown.ts +4 -0
  52. package/lib/components/element.d.ts +1 -0
  53. package/lib/components/element.d.ts.map +1 -1
  54. package/lib/components/element.js +3 -0
  55. package/lib/components/element.ts +4 -0
  56. package/lib/components/grid.d.ts +1 -0
  57. package/lib/components/grid.d.ts.map +1 -1
  58. package/lib/components/grid.js +3 -0
  59. package/lib/components/grid.ts +4 -0
  60. package/lib/components/heading.d.ts +1 -0
  61. package/lib/components/heading.d.ts.map +1 -1
  62. package/lib/components/heading.js +3 -0
  63. package/lib/components/heading.ts +4 -0
  64. package/lib/components/hero.d.ts +3 -5
  65. package/lib/components/hero.d.ts.map +1 -1
  66. package/lib/components/hero.js +1 -1
  67. package/lib/components/hero.ts +4 -6
  68. package/lib/components/icon.d.ts +1 -0
  69. package/lib/components/icon.d.ts.map +1 -1
  70. package/lib/components/icon.js +3 -0
  71. package/lib/components/icon.ts +4 -0
  72. package/lib/components/list.d.ts +1 -0
  73. package/lib/components/list.d.ts.map +1 -1
  74. package/lib/components/list.js +3 -0
  75. package/lib/components/list.ts +4 -0
  76. package/lib/components/loading.d.ts +1 -0
  77. package/lib/components/loading.d.ts.map +1 -1
  78. package/lib/components/loading.js +3 -0
  79. package/lib/components/loading.ts +4 -0
  80. package/lib/components/menu.d.ts +1 -0
  81. package/lib/components/menu.d.ts.map +1 -1
  82. package/lib/components/menu.js +3 -0
  83. package/lib/components/menu.ts +4 -0
  84. package/lib/components/modal.d.ts +1 -0
  85. package/lib/components/modal.d.ts.map +1 -1
  86. package/lib/components/modal.js +3 -0
  87. package/lib/components/modal.ts +4 -0
  88. package/lib/components/nav.d.ts +1 -0
  89. package/lib/components/nav.d.ts.map +1 -1
  90. package/lib/components/nav.js +3 -0
  91. package/lib/components/nav.ts +4 -0
  92. package/lib/components/paragraph.d.ts +1 -0
  93. package/lib/components/paragraph.d.ts.map +1 -1
  94. package/lib/components/paragraph.js +3 -0
  95. package/lib/components/paragraph.ts +4 -0
  96. package/lib/components/progress.d.ts +1 -0
  97. package/lib/components/progress.d.ts.map +1 -1
  98. package/lib/components/progress.js +3 -0
  99. package/lib/components/progress.ts +4 -0
  100. package/lib/components/sidebar.d.ts +25 -14
  101. package/lib/components/sidebar.d.ts.map +1 -1
  102. package/lib/components/sidebar.js +186 -88
  103. package/lib/components/sidebar.ts +231 -104
  104. package/lib/components/table.d.ts +1 -0
  105. package/lib/components/table.d.ts.map +1 -1
  106. package/lib/components/table.js +3 -0
  107. package/lib/components/table.ts +4 -0
  108. package/lib/components/tabs.d.ts +1 -0
  109. package/lib/components/tabs.d.ts.map +1 -1
  110. package/lib/components/tabs.js +3 -0
  111. package/lib/components/tabs.ts +4 -0
  112. package/lib/components/theme-toggle.d.ts +1 -0
  113. package/lib/components/theme-toggle.d.ts.map +1 -1
  114. package/lib/components/theme-toggle.js +3 -0
  115. package/lib/components/theme-toggle.ts +4 -0
  116. package/lib/components/tooltip.d.ts +1 -0
  117. package/lib/components/tooltip.d.ts.map +1 -1
  118. package/lib/components/tooltip.js +3 -0
  119. package/lib/components/tooltip.ts +4 -0
  120. package/package.json +1 -1
  121. package/lib/components/guard.d.ts +0 -41
  122. package/lib/components/guard.d.ts.map +0 -1
  123. package/lib/components/guard.js +0 -56
  124. package/lib/components/guard.ts +0 -92
@@ -1,9 +1,9 @@
1
1
  import { BaseComponent } from './base/BaseComponent.js';
2
2
  import { renderIcon } from './icons.js';
3
- import { Menu } from './menu.js';
3
+ import { req } from './req.js';
4
4
  // Event definitions
5
5
  const TRIGGER_EVENTS = [];
6
- const CALLBACK_EVENTS = ['toggle'];
6
+ const CALLBACK_EVENTS = ['toggle', 'itemClick'];
7
7
  export class Sidebar extends BaseComponent {
8
8
  constructor(id, options = {}) {
9
9
  // Restore collapsed state from localStorage if available
@@ -17,15 +17,17 @@ export class Sidebar extends BaseComponent {
17
17
  showToggle: options.showToggle ?? false,
18
18
  expandOnHover: options.expandOnHover ?? false,
19
19
  collapsed: initialCollapsed,
20
- menu: options.menu ?? null,
20
+ items: options.items ?? [],
21
+ header: options.header ?? '',
22
+ footer: options.footer ?? '',
21
23
  style: options.style ?? '',
22
24
  class: options.class ?? ''
23
25
  });
24
26
  this._sidebar = null;
25
- this._menu = null;
26
- // Listen for URL changes to update menu active states
27
+ this._setActiveStates();
28
+ // Listen for URL changes to update active states
27
29
  if (typeof window !== 'undefined') {
28
- window.addEventListener('popstate', () => this.refreshMenu());
30
+ window.addEventListener('popstate', () => this.refreshActiveStates());
29
31
  }
30
32
  }
31
33
  getTriggerEvents() {
@@ -34,19 +36,140 @@ export class Sidebar extends BaseComponent {
34
36
  getCallbackEvents() {
35
37
  return CALLBACK_EVENTS;
36
38
  }
39
+ /* ═════════════════════════════════════════════════════════════════
40
+ * PRIVATE HELPERS
41
+ * ═════════════════════════════════════════════════════════════════ */
42
+ _setActiveStates() {
43
+ this.state.items = this.state.items.map(item => ({
44
+ ...item,
45
+ active: item.href ? req.isActiveNavItem(item.href) : false,
46
+ items: item.items?.map(subItem => ({
47
+ ...subItem,
48
+ active: subItem.href ? req.isActiveNavItem(subItem.href) : false
49
+ }))
50
+ }));
51
+ }
52
+ _renderItem(item) {
53
+ const itemEl = document.createElement('div');
54
+ itemEl.className = 'jux-sidebar-item';
55
+ if (item.itemClass) {
56
+ itemEl.className += ` ${item.itemClass}`;
57
+ }
58
+ if (item.active) {
59
+ itemEl.classList.add('jux-sidebar-item-active');
60
+ }
61
+ if (item.href) {
62
+ const link = document.createElement('a');
63
+ link.className = 'jux-sidebar-link';
64
+ link.href = item.href;
65
+ if (item.active) {
66
+ link.classList.add('jux-sidebar-link-active');
67
+ }
68
+ if (item.icon) {
69
+ const icon = document.createElement('span');
70
+ icon.className = 'jux-sidebar-icon';
71
+ icon.appendChild(renderIcon(item.icon));
72
+ link.appendChild(icon);
73
+ }
74
+ const label = document.createElement('span');
75
+ label.className = 'jux-sidebar-label';
76
+ label.textContent = item.label;
77
+ link.appendChild(label);
78
+ link.addEventListener('click', (e) => {
79
+ this._triggerCallback('itemClick', { item, event: e });
80
+ });
81
+ itemEl.appendChild(link);
82
+ }
83
+ else {
84
+ const button = document.createElement('button');
85
+ button.className = 'jux-sidebar-button';
86
+ button.type = 'button';
87
+ if (item.icon) {
88
+ const icon = document.createElement('span');
89
+ icon.className = 'jux-sidebar-icon';
90
+ icon.appendChild(renderIcon(item.icon));
91
+ button.appendChild(icon);
92
+ }
93
+ const label = document.createElement('span');
94
+ label.className = 'jux-sidebar-label';
95
+ label.textContent = item.label;
96
+ button.appendChild(label);
97
+ button.addEventListener('click', (e) => {
98
+ if (item.click) {
99
+ item.click();
100
+ }
101
+ this._triggerCallback('itemClick', { item, event: e });
102
+ });
103
+ itemEl.appendChild(button);
104
+ }
105
+ // Handle sub-items
106
+ if (item.items && item.items.length > 0) {
107
+ itemEl.classList.add('jux-sidebar-item-has-submenu');
108
+ const submenu = document.createElement('div');
109
+ submenu.className = 'jux-sidebar-submenu';
110
+ item.items.forEach(subItem => {
111
+ submenu.appendChild(this._renderItem(subItem));
112
+ });
113
+ itemEl.appendChild(submenu);
114
+ // Toggle submenu expansion
115
+ const clickTarget = itemEl.querySelector('a, button');
116
+ if (clickTarget) {
117
+ clickTarget.addEventListener('click', (e) => {
118
+ if (!item.href) {
119
+ e.preventDefault();
120
+ }
121
+ itemEl.classList.toggle('jux-sidebar-item-expanded');
122
+ });
123
+ }
124
+ }
125
+ return itemEl;
126
+ }
127
+ /* ═════════════════════════════════════════════════════════════════
128
+ * REACTIVE UPDATE
129
+ * ═════════════════════════════════════════════════════════════════ */
130
+ update(prop, value) {
131
+ if (!this._sidebar)
132
+ return;
133
+ if (prop === 'collapsed') {
134
+ this._sidebar.classList.toggle('jux-sidebar-collapsed', value);
135
+ // Update toggle button state
136
+ const toggleBtn = this._sidebar.querySelector('.jux-sidebar-toggle');
137
+ if (toggleBtn) {
138
+ toggleBtn.classList.toggle('jux-sidebar-toggle-collapsed', value);
139
+ }
140
+ // Save to localStorage
141
+ const storageKey = `jux-sidebar-${this._id}-collapsed`;
142
+ localStorage.setItem(storageKey, String(value));
143
+ }
144
+ else if (prop === 'items') {
145
+ this._setActiveStates();
146
+ const nav = this._sidebar.querySelector('.jux-sidebar-nav');
147
+ if (nav) {
148
+ nav.innerHTML = '';
149
+ this.state.items.forEach(item => {
150
+ nav.appendChild(this._renderItem(item));
151
+ });
152
+ }
153
+ }
154
+ else if (prop === 'header') {
155
+ const header = this._sidebar.querySelector('.jux-sidebar-header');
156
+ if (header)
157
+ header.textContent = value;
158
+ }
159
+ else if (prop === 'footer') {
160
+ const footer = this._sidebar.querySelector('.jux-sidebar-footer');
161
+ if (footer)
162
+ footer.textContent = value;
163
+ }
164
+ }
37
165
  /* ═════════════════════════════════════════════════════════════════
38
166
  * FLUENT API
39
167
  * ═════════════════════════════════════════════════════════════════ */
40
- // ✅ Inherited from BaseComponent:
41
- // - style(), class()
42
- // - bind(), sync(), renderTo()
43
- // - addClass(), removeClass(), toggleClass()
44
- // - visible(), show(), hide()
45
- // - attr(), attrs(), removeAttr()
46
- // - disabled(), enable(), disable()
47
- // - loading(), focus(), blur(), remove()
48
168
  width(value) {
49
169
  this.state.width = value;
170
+ if (this._sidebar) {
171
+ this._sidebar.style.width = value;
172
+ }
50
173
  return this;
51
174
  }
52
175
  position(value) {
@@ -67,64 +190,78 @@ export class Sidebar extends BaseComponent {
67
190
  }
68
191
  collapsed(value) {
69
192
  this.state.collapsed = value;
70
- this._updateCollapsedState();
71
193
  return this;
72
194
  }
73
- menu(value) {
74
- this.state.menu = value;
195
+ items(value) {
196
+ this.state.items = value;
197
+ return this;
198
+ }
199
+ addItem(item) {
200
+ this.state.items = [...this.state.items, item];
201
+ return this;
202
+ }
203
+ header(value) {
204
+ this.state.header = value;
205
+ return this;
206
+ }
207
+ footer(value) {
208
+ this.state.footer = value;
75
209
  return this;
76
210
  }
77
211
  toggle() {
78
212
  this.state.collapsed = !this.state.collapsed;
79
- this._updateCollapsedState();
80
- // Save to localStorage
81
- const storageKey = `jux-sidebar-${this._id}-collapsed`;
82
- localStorage.setItem(storageKey, String(this.state.collapsed));
83
- // 🎯 Fire the toggle callback event
84
213
  this._triggerCallback('toggle', this.state.collapsed);
85
214
  }
86
- /**
87
- * Refresh menu active states (called automatically on URL changes)
88
- */
89
- refreshMenu() {
90
- if (this._menu && this.state.menu) {
91
- // Re-evaluate and update menu items with current active states
92
- this._menu.items(this.state.menu.items || []);
93
- }
215
+ refreshActiveStates() {
216
+ this._setActiveStates();
217
+ this.state.items = [...this.state.items]; // Trigger update
94
218
  return this;
95
219
  }
96
- _updateCollapsedState() {
97
- if (this._sidebar) {
98
- this._sidebar.classList.toggle('jux-sidebar-collapsed', this.state.collapsed);
99
- }
100
- }
101
220
  /* ═════════════════════════════════════════════════════════════════
102
221
  * RENDER
103
222
  * ═════════════════════════════════════════════════════════════════ */
104
223
  render(targetId) {
105
224
  const container = this._setupContainer(targetId);
106
- const { position, collapsed, expandOnHover, menu, style, class: className } = this.state;
107
- // Build sidebar element
225
+ const { position, collapsed, expandOnHover, items, header, footer, width, style, class: className } = this.state;
108
226
  const sidebar = document.createElement('aside');
227
+ sidebar.className = `jux-sidebar jux-sidebar-${position}`;
109
228
  sidebar.id = this._id;
229
+ if (collapsed) {
230
+ sidebar.classList.add('jux-sidebar-collapsed');
231
+ }
110
232
  if (className)
111
- sidebar.className = className;
233
+ sidebar.className += ` ${className}`;
112
234
  if (style)
113
235
  sidebar.setAttribute('style', style);
114
- // Apply initial collapsed state
115
- if (collapsed) {
116
- sidebar.classList.add('jux-sidebar-collapsed');
236
+ if (width)
237
+ sidebar.style.width = width;
238
+ // Header
239
+ if (header) {
240
+ const headerEl = document.createElement('div');
241
+ headerEl.className = 'jux-sidebar-header';
242
+ headerEl.textContent = header;
243
+ sidebar.appendChild(headerEl);
117
244
  }
118
- // Menu container (append first so toggle appears at bottom)
119
- const menuContainer = document.createElement('div');
120
- menuContainer.className = 'jux-sidebar-menu';
121
- menuContainer.id = `${this._id}-menu`;
122
- sidebar.appendChild(menuContainer);
123
- // Toggle button (append last so it appears at bottom)
245
+ // Navigation
246
+ const nav = document.createElement('nav');
247
+ nav.className = 'jux-sidebar-nav';
248
+ items.forEach(item => {
249
+ nav.appendChild(this._renderItem(item));
250
+ });
251
+ sidebar.appendChild(nav);
252
+ // Footer
253
+ if (footer) {
254
+ const footerEl = document.createElement('div');
255
+ footerEl.className = 'jux-sidebar-footer';
256
+ footerEl.textContent = footer;
257
+ sidebar.appendChild(footerEl);
258
+ }
259
+ // Toggle button
124
260
  if (this.state.collapsible || this.state.showToggle) {
125
261
  const toggleBtn = document.createElement('button');
126
262
  toggleBtn.className = 'jux-sidebar-toggle';
127
263
  toggleBtn.type = 'button';
264
+ toggleBtn.setAttribute('aria-label', 'Toggle sidebar');
128
265
  const toggleIcon = document.createElement('span');
129
266
  toggleIcon.className = 'jux-sidebar-toggle-icon';
130
267
  const chevronIcon = renderIcon(position === 'left' ? '➡️' : '⬅️');
@@ -135,11 +272,10 @@ export class Sidebar extends BaseComponent {
135
272
  }
136
273
  toggleBtn.addEventListener('click', () => {
137
274
  this.toggle();
138
- toggleBtn.classList.toggle('jux-sidebar-toggle-collapsed', this.state.collapsed);
139
275
  });
140
276
  sidebar.appendChild(toggleBtn);
141
277
  }
142
- // Handle expand on hover
278
+ // Expand on hover
143
279
  if (expandOnHover) {
144
280
  sidebar.addEventListener('mouseenter', () => {
145
281
  if (this.state.collapsed) {
@@ -152,48 +288,10 @@ export class Sidebar extends BaseComponent {
152
288
  }
153
289
  });
154
290
  }
155
- // Wire events using inherited method
156
291
  this._wireStandardEvents(sidebar);
157
- // Wire sync bindings for 'collapsed' property
158
- this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
159
- if (property === 'collapsed') {
160
- const transform = toComponent || ((v) => v);
161
- stateObj.subscribe((val) => {
162
- const transformed = transform(val);
163
- const isCollapsed = Boolean(transformed);
164
- this.state.collapsed = isCollapsed;
165
- this._updateCollapsedState();
166
- });
167
- }
168
- else if (property === 'menu') {
169
- const transform = toComponent || ((v) => v);
170
- stateObj.subscribe((val) => {
171
- const transformed = transform(val);
172
- this.state.menu = transformed;
173
- // Update menu items if menu instance exists
174
- if (this._menu) {
175
- this._menu.items(transformed.items || []);
176
- // Re-render the menu
177
- const menuEl = menuContainer.querySelector(`#${this._id}-menu-instance`);
178
- if (menuEl) {
179
- menuEl.remove();
180
- }
181
- this._menu.render(`#${menuContainer.id}`);
182
- }
183
- });
184
- }
185
- });
292
+ this._wireAllSyncs();
186
293
  container.appendChild(sidebar);
187
294
  this._sidebar = sidebar;
188
- // Create and render menu (after sidebar is in DOM)
189
- if (menu) {
190
- this._menu = new Menu(`${this._id}-menu-instance`, {
191
- ...menu,
192
- orientation: 'vertical'
193
- });
194
- this._menu.render(`#${menuContainer.id}`);
195
- }
196
- // Trigger Iconify icon rendering
197
295
  requestAnimationFrame(() => {
198
296
  if (window.Iconify) {
199
297
  window.Iconify.scan();