juxscript 1.1.14 → 1.1.16
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/index.d.ts +0 -2
- package/index.d.ts.map +1 -1
- package/index.js +0 -2
- package/lib/components/alert.d.ts.map +1 -1
- package/lib/components/alert.js +14 -1
- package/lib/components/alert.ts +13 -1
- package/lib/components/badge.d.ts +2 -4
- package/lib/components/badge.d.ts.map +1 -1
- package/lib/components/badge.js +18 -1
- package/lib/components/badge.ts +17 -5
- package/lib/components/base/BaseComponent.d.ts +18 -2
- package/lib/components/base/BaseComponent.d.ts.map +1 -1
- package/lib/components/base/BaseComponent.js +25 -41
- package/lib/components/base/BaseComponent.ts +43 -42
- package/lib/components/hero.d.ts +2 -4
- package/lib/components/hero.d.ts.map +1 -1
- package/lib/components/hero.js +6 -10
- package/lib/components/hero.ts +8 -12
- package/lib/components/sidebar.d.ts +25 -15
- package/lib/components/sidebar.d.ts.map +1 -1
- package/lib/components/sidebar.js +179 -91
- package/lib/components/sidebar.ts +224 -108
- package/package.json +1 -1
- package/lib/components/guard.d.ts +0 -42
- package/lib/components/guard.d.ts.map +0 -1
- package/lib/components/guard.js +0 -59
- package/lib/components/guard.ts +0 -96
package/lib/components/hero.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseComponent } from './base/BaseComponent.js';
|
|
1
|
+
import { BaseComponent, BaseState } from './base/BaseComponent.js';
|
|
2
2
|
|
|
3
3
|
// Event definitions - Hero is display-only, but CTA button can trigger actions
|
|
4
4
|
const TRIGGER_EVENTS = [] as const;
|
|
@@ -18,7 +18,7 @@ export interface HeroOptions {
|
|
|
18
18
|
class?: string;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
type HeroState = {
|
|
21
|
+
type HeroState = BaseState & {
|
|
22
22
|
title: string;
|
|
23
23
|
subtitle: string;
|
|
24
24
|
content: string;
|
|
@@ -28,8 +28,6 @@ type HeroState = {
|
|
|
28
28
|
backgroundOverlay: boolean;
|
|
29
29
|
variant: string;
|
|
30
30
|
centered: boolean;
|
|
31
|
-
style: string;
|
|
32
|
-
class: string;
|
|
33
31
|
};
|
|
34
32
|
|
|
35
33
|
export class Hero extends BaseComponent<HeroState> {
|
|
@@ -71,7 +69,7 @@ export class Hero extends BaseComponent<HeroState> {
|
|
|
71
69
|
*/
|
|
72
70
|
update(prop: string, value: any): void {
|
|
73
71
|
const hero = document.getElementById(this._id);
|
|
74
|
-
if (!hero) return;
|
|
72
|
+
if (!hero) return;
|
|
75
73
|
|
|
76
74
|
switch (prop) {
|
|
77
75
|
case 'title':
|
|
@@ -85,17 +83,17 @@ export class Hero extends BaseComponent<HeroState> {
|
|
|
85
83
|
break;
|
|
86
84
|
|
|
87
85
|
case 'content':
|
|
88
|
-
const contentEl = hero.querySelector('.jux-hero-body');
|
|
86
|
+
const contentEl = hero.querySelector('.jux-hero-body'); // ✅ BEM naming
|
|
89
87
|
if (contentEl) contentEl.innerHTML = value;
|
|
90
88
|
break;
|
|
91
89
|
|
|
92
90
|
case 'cta':
|
|
93
|
-
const ctaEl = hero.querySelector('.jux-hero-cta') as HTMLElement;
|
|
91
|
+
const ctaEl = hero.querySelector('.jux-hero-cta') as HTMLElement; // ✅ BEM naming
|
|
94
92
|
if (ctaEl) ctaEl.textContent = value;
|
|
95
93
|
break;
|
|
96
94
|
|
|
97
95
|
case 'ctaLink':
|
|
98
|
-
const ctaLinkEl = hero.querySelector('.jux-hero-cta') as HTMLAnchorElement;
|
|
96
|
+
const ctaLinkEl = hero.querySelector('.jux-hero-cta') as HTMLAnchorElement; // ✅ BEM naming
|
|
99
97
|
if (ctaLinkEl) ctaLinkEl.href = value;
|
|
100
98
|
break;
|
|
101
99
|
|
|
@@ -106,8 +104,7 @@ export class Hero extends BaseComponent<HeroState> {
|
|
|
106
104
|
break;
|
|
107
105
|
|
|
108
106
|
case 'centered':
|
|
109
|
-
|
|
110
|
-
else hero.classList.remove('jux-hero-centered');
|
|
107
|
+
hero.classList.toggle('jux-hero-centered', value); // ✅ BEM modifier
|
|
111
108
|
break;
|
|
112
109
|
}
|
|
113
110
|
}
|
|
@@ -228,7 +225,6 @@ export class Hero extends BaseComponent<HeroState> {
|
|
|
228
225
|
ctaButton.href = this.state.ctaLink;
|
|
229
226
|
ctaButton.textContent = this.state.cta;
|
|
230
227
|
|
|
231
|
-
// ✅ Fire callback when CTA is clicked
|
|
232
228
|
ctaButton.addEventListener('click', (e) => {
|
|
233
229
|
this._triggerCallback('ctaClick', e);
|
|
234
230
|
});
|
|
@@ -239,7 +235,7 @@ export class Hero extends BaseComponent<HeroState> {
|
|
|
239
235
|
hero.appendChild(contentContainer);
|
|
240
236
|
|
|
241
237
|
this._wireStandardEvents(hero);
|
|
242
|
-
this._wireAllSyncs();
|
|
238
|
+
this._wireAllSyncs();
|
|
243
239
|
|
|
244
240
|
container.appendChild(hero);
|
|
245
241
|
return this;
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import { BaseComponent } from './base/BaseComponent.js';
|
|
2
|
-
|
|
1
|
+
import { BaseComponent, BaseState } from './base/BaseComponent.js';
|
|
2
|
+
export interface SidebarItem {
|
|
3
|
+
label: string;
|
|
4
|
+
href?: string;
|
|
5
|
+
click?: () => void;
|
|
6
|
+
icon?: string;
|
|
7
|
+
items?: SidebarItem[];
|
|
8
|
+
active?: boolean;
|
|
9
|
+
itemClass?: string;
|
|
10
|
+
}
|
|
3
11
|
export interface SidebarOptions {
|
|
4
12
|
position?: 'left' | 'right';
|
|
5
13
|
width?: string;
|
|
@@ -7,42 +15,44 @@ export interface SidebarOptions {
|
|
|
7
15
|
showToggle?: boolean;
|
|
8
16
|
expandOnHover?: boolean;
|
|
9
17
|
collapsed?: boolean;
|
|
10
|
-
|
|
18
|
+
items?: SidebarItem[];
|
|
19
|
+
header?: string;
|
|
20
|
+
footer?: string;
|
|
11
21
|
style?: string;
|
|
12
22
|
class?: string;
|
|
13
23
|
}
|
|
14
|
-
type SidebarState = {
|
|
24
|
+
type SidebarState = BaseState & {
|
|
15
25
|
position: 'left' | 'right';
|
|
16
26
|
width: string;
|
|
17
27
|
collapsible: boolean;
|
|
18
28
|
showToggle: boolean;
|
|
19
29
|
expandOnHover: boolean;
|
|
20
30
|
collapsed: boolean;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
31
|
+
items: SidebarItem[];
|
|
32
|
+
header: string;
|
|
33
|
+
footer: string;
|
|
24
34
|
};
|
|
25
35
|
export declare class Sidebar extends BaseComponent<SidebarState> {
|
|
26
36
|
private _sidebar;
|
|
27
|
-
private _menu;
|
|
28
37
|
constructor(id: string, options?: SidebarOptions);
|
|
29
38
|
protected getTriggerEvents(): readonly string[];
|
|
30
39
|
protected getCallbackEvents(): readonly string[];
|
|
40
|
+
private _setActiveStates;
|
|
41
|
+
private _renderItem;
|
|
42
|
+
update(prop: string, value: any): void;
|
|
31
43
|
width(value: string): this;
|
|
32
44
|
position(value: 'left' | 'right'): this;
|
|
33
45
|
collapsible(value: boolean): this;
|
|
34
46
|
showToggle(value: boolean): this;
|
|
35
47
|
expandOnHover(value: boolean): this;
|
|
36
48
|
collapsed(value: boolean): this;
|
|
37
|
-
|
|
49
|
+
items(value: SidebarItem[]): this;
|
|
50
|
+
addItem(item: SidebarItem): this;
|
|
51
|
+
header(value: string): this;
|
|
52
|
+
footer(value: string): this;
|
|
38
53
|
toggle(): void;
|
|
39
|
-
|
|
40
|
-
* Refresh menu active states (called automatically on URL changes)
|
|
41
|
-
*/
|
|
42
|
-
refreshMenu(): this;
|
|
43
|
-
private _updateCollapsedState;
|
|
54
|
+
refreshActiveStates(): this;
|
|
44
55
|
render(targetId?: string): this;
|
|
45
|
-
update(prop: string, value: any): void;
|
|
46
56
|
}
|
|
47
57
|
export declare function sidebar(id: string, options?: SidebarOptions): Sidebar;
|
|
48
58
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidebar.d.ts","sourceRoot":"","sources":["sidebar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"sidebar.d.ts","sourceRoot":"","sources":["sidebar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAQnE,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,YAAY,GAAG,SAAS,GAAG;IAC9B,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,qBAAa,OAAQ,SAAQ,aAAa,CAAC,YAAY,CAAC;IACtD,OAAO,CAAC,QAAQ,CAA4B;gBAEhC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB;IA4BpD,SAAS,CAAC,gBAAgB,IAAI,SAAS,MAAM,EAAE;IAI/C,SAAS,CAAC,iBAAiB,IAAI,SAAS,MAAM,EAAE;IAQhD,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,WAAW;IA+FnB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAsCtC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAQ1B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI;IAKvC,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAKjC,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAKhC,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAKnC,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAK/B,KAAK,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,IAAI;IAKjC,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI;IAKhC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK3B,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK3B,MAAM,IAAI,IAAI;IAKd,mBAAmB,IAAI,IAAI;IAU3B,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;CA2FhC;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAEzE"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { BaseComponent } from './base/BaseComponent.js';
|
|
2
2
|
import { renderIcon } from './icons.js';
|
|
3
|
-
import {
|
|
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
|
-
|
|
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.
|
|
26
|
-
// Listen for URL changes to update
|
|
27
|
+
this._setActiveStates();
|
|
28
|
+
// Listen for URL changes to update active states
|
|
27
29
|
if (typeof window !== 'undefined') {
|
|
28
|
-
window.addEventListener('popstate', () => this.
|
|
30
|
+
window.addEventListener('popstate', () => this.refreshActiveStates());
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
33
|
getTriggerEvents() {
|
|
@@ -34,19 +36,138 @@ 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
|
+
if (item.items && item.items.length > 0) {
|
|
106
|
+
itemEl.classList.add('jux-sidebar-item-has-submenu');
|
|
107
|
+
const submenu = document.createElement('div');
|
|
108
|
+
submenu.className = 'jux-sidebar-submenu';
|
|
109
|
+
item.items.forEach(subItem => {
|
|
110
|
+
submenu.appendChild(this._renderItem(subItem));
|
|
111
|
+
});
|
|
112
|
+
itemEl.appendChild(submenu);
|
|
113
|
+
const clickTarget = itemEl.querySelector('a, button');
|
|
114
|
+
if (clickTarget) {
|
|
115
|
+
clickTarget.addEventListener('click', (e) => {
|
|
116
|
+
if (!item.href) {
|
|
117
|
+
e.preventDefault();
|
|
118
|
+
}
|
|
119
|
+
itemEl.classList.toggle('jux-sidebar-item-expanded');
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return itemEl;
|
|
124
|
+
}
|
|
125
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
126
|
+
* REACTIVE UPDATE
|
|
127
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
128
|
+
update(prop, value) {
|
|
129
|
+
if (!this._sidebar)
|
|
130
|
+
return;
|
|
131
|
+
if (prop === 'collapsed') {
|
|
132
|
+
this._sidebar.classList.toggle('jux-sidebar-collapsed', value);
|
|
133
|
+
// Update toggle button state
|
|
134
|
+
const toggleBtn = this._sidebar.querySelector('.jux-sidebar-toggle');
|
|
135
|
+
if (toggleBtn) {
|
|
136
|
+
toggleBtn.classList.toggle('jux-sidebar-toggle-collapsed', value);
|
|
137
|
+
}
|
|
138
|
+
// Save to localStorage
|
|
139
|
+
const storageKey = `jux-sidebar-${this._id}-collapsed`;
|
|
140
|
+
localStorage.setItem(storageKey, String(value));
|
|
141
|
+
}
|
|
142
|
+
else if (prop === 'items') {
|
|
143
|
+
this._setActiveStates();
|
|
144
|
+
const nav = this._sidebar.querySelector('.jux-sidebar-nav');
|
|
145
|
+
if (nav) {
|
|
146
|
+
nav.innerHTML = '';
|
|
147
|
+
this.state.items.forEach(item => {
|
|
148
|
+
nav.appendChild(this._renderItem(item));
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else if (prop === 'header') {
|
|
153
|
+
const header = this._sidebar.querySelector('.jux-sidebar-header');
|
|
154
|
+
if (header)
|
|
155
|
+
header.textContent = value;
|
|
156
|
+
}
|
|
157
|
+
else if (prop === 'footer') {
|
|
158
|
+
const footer = this._sidebar.querySelector('.jux-sidebar-footer');
|
|
159
|
+
if (footer)
|
|
160
|
+
footer.textContent = value;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
37
163
|
/* ═════════════════════════════════════════════════════════════════
|
|
38
164
|
* FLUENT API
|
|
39
165
|
* ═════════════════════════════════════════════════════════════════ */
|
|
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
166
|
width(value) {
|
|
49
167
|
this.state.width = value;
|
|
168
|
+
if (this._sidebar) {
|
|
169
|
+
this._sidebar.style.width = value;
|
|
170
|
+
}
|
|
50
171
|
return this;
|
|
51
172
|
}
|
|
52
173
|
position(value) {
|
|
@@ -67,64 +188,74 @@ export class Sidebar extends BaseComponent {
|
|
|
67
188
|
}
|
|
68
189
|
collapsed(value) {
|
|
69
190
|
this.state.collapsed = value;
|
|
70
|
-
this._updateCollapsedState();
|
|
71
191
|
return this;
|
|
72
192
|
}
|
|
73
|
-
|
|
74
|
-
this.state.
|
|
193
|
+
items(value) {
|
|
194
|
+
this.state.items = value;
|
|
195
|
+
return this;
|
|
196
|
+
}
|
|
197
|
+
addItem(item) {
|
|
198
|
+
this.state.items = [...this.state.items, item];
|
|
199
|
+
return this;
|
|
200
|
+
}
|
|
201
|
+
header(value) {
|
|
202
|
+
this.state.header = value;
|
|
203
|
+
return this;
|
|
204
|
+
}
|
|
205
|
+
footer(value) {
|
|
206
|
+
this.state.footer = value;
|
|
75
207
|
return this;
|
|
76
208
|
}
|
|
77
209
|
toggle() {
|
|
78
210
|
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
211
|
this._triggerCallback('toggle', this.state.collapsed);
|
|
85
212
|
}
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
}
|
|
213
|
+
refreshActiveStates() {
|
|
214
|
+
this._setActiveStates();
|
|
215
|
+
this.state.items = [...this.state.items]; // Trigger update
|
|
94
216
|
return this;
|
|
95
217
|
}
|
|
96
|
-
_updateCollapsedState() {
|
|
97
|
-
if (this._sidebar) {
|
|
98
|
-
this._sidebar.classList.toggle('jux-sidebar-collapsed', this.state.collapsed);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
218
|
/* ═════════════════════════════════════════════════════════════════
|
|
102
219
|
* RENDER
|
|
103
220
|
* ═════════════════════════════════════════════════════════════════ */
|
|
104
221
|
render(targetId) {
|
|
105
222
|
const container = this._setupContainer(targetId);
|
|
106
|
-
const { position, collapsed, expandOnHover,
|
|
107
|
-
// Build sidebar element
|
|
223
|
+
const { position, collapsed, expandOnHover, items, header, footer, width, style, class: className } = this.state;
|
|
108
224
|
const sidebar = document.createElement('aside');
|
|
225
|
+
sidebar.className = `jux-sidebar jux-sidebar-${position}`;
|
|
109
226
|
sidebar.id = this._id;
|
|
227
|
+
if (collapsed) {
|
|
228
|
+
sidebar.classList.add('jux-sidebar-collapsed');
|
|
229
|
+
}
|
|
110
230
|
if (className)
|
|
111
|
-
sidebar.className
|
|
231
|
+
sidebar.className += ` ${className}`;
|
|
112
232
|
if (style)
|
|
113
233
|
sidebar.setAttribute('style', style);
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
234
|
+
if (width)
|
|
235
|
+
sidebar.style.width = width;
|
|
236
|
+
if (header) {
|
|
237
|
+
const headerEl = document.createElement('div');
|
|
238
|
+
headerEl.className = 'jux-sidebar-header';
|
|
239
|
+
headerEl.textContent = header;
|
|
240
|
+
sidebar.appendChild(headerEl);
|
|
241
|
+
}
|
|
242
|
+
const nav = document.createElement('nav');
|
|
243
|
+
nav.className = 'jux-sidebar-nav';
|
|
244
|
+
items.forEach(item => {
|
|
245
|
+
nav.appendChild(this._renderItem(item));
|
|
246
|
+
});
|
|
247
|
+
sidebar.appendChild(nav);
|
|
248
|
+
if (footer) {
|
|
249
|
+
const footerEl = document.createElement('div');
|
|
250
|
+
footerEl.className = 'jux-sidebar-footer';
|
|
251
|
+
footerEl.textContent = footer;
|
|
252
|
+
sidebar.appendChild(footerEl);
|
|
117
253
|
}
|
|
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)
|
|
124
254
|
if (this.state.collapsible || this.state.showToggle) {
|
|
125
255
|
const toggleBtn = document.createElement('button');
|
|
126
256
|
toggleBtn.className = 'jux-sidebar-toggle';
|
|
127
257
|
toggleBtn.type = 'button';
|
|
258
|
+
toggleBtn.setAttribute('aria-label', 'Toggle sidebar');
|
|
128
259
|
const toggleIcon = document.createElement('span');
|
|
129
260
|
toggleIcon.className = 'jux-sidebar-toggle-icon';
|
|
130
261
|
const chevronIcon = renderIcon(position === 'left' ? '➡️' : '⬅️');
|
|
@@ -135,11 +266,9 @@ export class Sidebar extends BaseComponent {
|
|
|
135
266
|
}
|
|
136
267
|
toggleBtn.addEventListener('click', () => {
|
|
137
268
|
this.toggle();
|
|
138
|
-
toggleBtn.classList.toggle('jux-sidebar-toggle-collapsed', this.state.collapsed);
|
|
139
269
|
});
|
|
140
270
|
sidebar.appendChild(toggleBtn);
|
|
141
271
|
}
|
|
142
|
-
// Handle expand on hover
|
|
143
272
|
if (expandOnHover) {
|
|
144
273
|
sidebar.addEventListener('mouseenter', () => {
|
|
145
274
|
if (this.state.collapsed) {
|
|
@@ -152,48 +281,10 @@ export class Sidebar extends BaseComponent {
|
|
|
152
281
|
}
|
|
153
282
|
});
|
|
154
283
|
}
|
|
155
|
-
// Wire events using inherited method
|
|
156
284
|
this._wireStandardEvents(sidebar);
|
|
157
|
-
|
|
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
|
-
});
|
|
285
|
+
this._wireAllSyncs();
|
|
186
286
|
container.appendChild(sidebar);
|
|
187
287
|
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
288
|
requestAnimationFrame(() => {
|
|
198
289
|
if (window.Iconify) {
|
|
199
290
|
window.Iconify.scan();
|
|
@@ -201,9 +292,6 @@ export class Sidebar extends BaseComponent {
|
|
|
201
292
|
});
|
|
202
293
|
return this;
|
|
203
294
|
}
|
|
204
|
-
update(prop, value) {
|
|
205
|
-
// No reactive updates needed
|
|
206
|
-
}
|
|
207
295
|
}
|
|
208
296
|
export function sidebar(id, options = {}) {
|
|
209
297
|
return new Sidebar(id, options);
|