juxscript 1.1.14 → 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.
@@ -1,11 +1,20 @@
1
- import { BaseComponent } from './base/BaseComponent.js';
1
+ import { BaseComponent, BaseState } from './base/BaseComponent.js';
2
2
  import { renderIcon } from './icons.js';
3
- import { Menu, MenuOptions } from './menu.js';
4
-
3
+ import { req } from './req.js';
5
4
 
6
5
  // Event definitions
7
6
  const TRIGGER_EVENTS = [] as const;
8
- const CALLBACK_EVENTS = ['toggle'] as const;
7
+ const CALLBACK_EVENTS = ['toggle', 'itemClick'] as const;
8
+
9
+ export interface SidebarItem {
10
+ label: string;
11
+ href?: string;
12
+ click?: () => void;
13
+ icon?: string;
14
+ items?: SidebarItem[];
15
+ active?: boolean;
16
+ itemClass?: string;
17
+ }
9
18
 
10
19
  export interface SidebarOptions {
11
20
  position?: 'left' | 'right';
@@ -14,26 +23,27 @@ export interface SidebarOptions {
14
23
  showToggle?: boolean;
15
24
  expandOnHover?: boolean;
16
25
  collapsed?: boolean;
17
- menu?: MenuOptions;
26
+ items?: SidebarItem[];
27
+ header?: string;
28
+ footer?: string;
18
29
  style?: string;
19
30
  class?: string;
20
31
  }
21
32
 
22
- type SidebarState = {
33
+ type SidebarState = BaseState & {
23
34
  position: 'left' | 'right';
24
35
  width: string;
25
36
  collapsible: boolean;
26
37
  showToggle: boolean;
27
38
  expandOnHover: boolean;
28
39
  collapsed: boolean;
29
- menu: MenuOptions | null;
30
- style: string;
31
- class: string;
40
+ items: SidebarItem[];
41
+ header: string;
42
+ footer: string;
32
43
  };
33
44
 
34
45
  export class Sidebar extends BaseComponent<SidebarState> {
35
46
  private _sidebar: HTMLElement | null = null;
36
- private _menu: Menu | null = null;
37
47
 
38
48
  constructor(id: string, options: SidebarOptions = {}) {
39
49
  // Restore collapsed state from localStorage if available
@@ -48,14 +58,18 @@ export class Sidebar extends BaseComponent<SidebarState> {
48
58
  showToggle: options.showToggle ?? false,
49
59
  expandOnHover: options.expandOnHover ?? false,
50
60
  collapsed: initialCollapsed,
51
- menu: options.menu ?? null,
61
+ items: options.items ?? [],
62
+ header: options.header ?? '',
63
+ footer: options.footer ?? '',
52
64
  style: options.style ?? '',
53
65
  class: options.class ?? ''
54
66
  });
55
67
 
56
- // Listen for URL changes to update menu active states
68
+ this._setActiveStates();
69
+
70
+ // Listen for URL changes to update active states
57
71
  if (typeof window !== 'undefined') {
58
- window.addEventListener('popstate', () => this.refreshMenu());
72
+ window.addEventListener('popstate', () => this.refreshActiveStates());
59
73
  }
60
74
  }
61
75
 
@@ -68,20 +82,160 @@ export class Sidebar extends BaseComponent<SidebarState> {
68
82
  }
69
83
 
70
84
  /* ═════════════════════════════════════════════════════════════════
71
- * FLUENT API
85
+ * PRIVATE HELPERS
72
86
  * ═════════════════════════════════════════════════════════════════ */
73
87
 
74
- // Inherited from BaseComponent:
75
- // - style(), class()
76
- // - bind(), sync(), renderTo()
77
- // - addClass(), removeClass(), toggleClass()
78
- // - visible(), show(), hide()
79
- // - attr(), attrs(), removeAttr()
80
- // - disabled(), enable(), disable()
81
- // - loading(), focus(), blur(), remove()
88
+ private _setActiveStates(): void {
89
+ this.state.items = this.state.items.map(item => ({
90
+ ...item,
91
+ active: item.href ? req.isActiveNavItem(item.href) : false,
92
+ items: item.items?.map(subItem => ({
93
+ ...subItem,
94
+ active: subItem.href ? req.isActiveNavItem(subItem.href) : false
95
+ }))
96
+ }));
97
+ }
98
+
99
+ private _renderItem(item: SidebarItem): HTMLElement {
100
+ const itemEl = document.createElement('div');
101
+ itemEl.className = 'jux-sidebar-item';
102
+
103
+ if (item.itemClass) {
104
+ itemEl.className += ` ${item.itemClass}`;
105
+ }
106
+
107
+ if (item.active) {
108
+ itemEl.classList.add('jux-sidebar-item-active');
109
+ }
110
+
111
+ if (item.href) {
112
+ const link = document.createElement('a');
113
+ link.className = 'jux-sidebar-link';
114
+ link.href = item.href;
115
+
116
+ if (item.active) {
117
+ link.classList.add('jux-sidebar-link-active');
118
+ }
119
+
120
+ if (item.icon) {
121
+ const icon = document.createElement('span');
122
+ icon.className = 'jux-sidebar-icon';
123
+ icon.appendChild(renderIcon(item.icon));
124
+ link.appendChild(icon);
125
+ }
126
+
127
+ const label = document.createElement('span');
128
+ label.className = 'jux-sidebar-label';
129
+ label.textContent = item.label;
130
+ link.appendChild(label);
131
+
132
+ link.addEventListener('click', (e) => {
133
+ this._triggerCallback('itemClick', { item, event: e });
134
+ });
135
+
136
+ itemEl.appendChild(link);
137
+ } else {
138
+ const button = document.createElement('button');
139
+ button.className = 'jux-sidebar-button';
140
+ button.type = 'button';
141
+
142
+ if (item.icon) {
143
+ const icon = document.createElement('span');
144
+ icon.className = 'jux-sidebar-icon';
145
+ icon.appendChild(renderIcon(item.icon));
146
+ button.appendChild(icon);
147
+ }
148
+
149
+ const label = document.createElement('span');
150
+ label.className = 'jux-sidebar-label';
151
+ label.textContent = item.label;
152
+ button.appendChild(label);
153
+
154
+ button.addEventListener('click', (e) => {
155
+ if (item.click) {
156
+ item.click();
157
+ }
158
+ this._triggerCallback('itemClick', { item, event: e });
159
+ });
160
+
161
+ itemEl.appendChild(button);
162
+ }
163
+
164
+ // Handle sub-items
165
+ if (item.items && item.items.length > 0) {
166
+ itemEl.classList.add('jux-sidebar-item-has-submenu');
167
+
168
+ const submenu = document.createElement('div');
169
+ submenu.className = 'jux-sidebar-submenu';
170
+
171
+ item.items.forEach(subItem => {
172
+ submenu.appendChild(this._renderItem(subItem));
173
+ });
174
+
175
+ itemEl.appendChild(submenu);
176
+
177
+ // Toggle submenu expansion
178
+ const clickTarget = itemEl.querySelector('a, button');
179
+ if (clickTarget) {
180
+ clickTarget.addEventListener('click', (e) => {
181
+ if (!item.href) {
182
+ e.preventDefault();
183
+ }
184
+ itemEl.classList.toggle('jux-sidebar-item-expanded');
185
+ });
186
+ }
187
+ }
188
+
189
+ return itemEl;
190
+ }
191
+
192
+ /* ═════════════════════════════════════════════════════════════════
193
+ * REACTIVE UPDATE
194
+ * ═════════════════════════════════════════════════════════════════ */
195
+
196
+ update(prop: string, value: any): void {
197
+
198
+ if (!this._sidebar) return;
199
+
200
+ if (prop === 'collapsed') {
201
+ this._sidebar.classList.toggle('jux-sidebar-collapsed', value);
202
+
203
+ // Update toggle button state
204
+ const toggleBtn = this._sidebar.querySelector('.jux-sidebar-toggle');
205
+ if (toggleBtn) {
206
+ toggleBtn.classList.toggle('jux-sidebar-toggle-collapsed', value);
207
+ }
208
+
209
+ // Save to localStorage
210
+ const storageKey = `jux-sidebar-${this._id}-collapsed`;
211
+ localStorage.setItem(storageKey, String(value));
212
+ } else if (prop === 'items') {
213
+ this._setActiveStates();
214
+ const nav = this._sidebar.querySelector('.jux-sidebar-nav');
215
+ if (nav) {
216
+ nav.innerHTML = '';
217
+ this.state.items.forEach(item => {
218
+ nav.appendChild(this._renderItem(item));
219
+ });
220
+ }
221
+ } else if (prop === 'header') {
222
+ const header = this._sidebar.querySelector('.jux-sidebar-header');
223
+ if (header) header.textContent = value;
224
+ } else if (prop === 'footer') {
225
+ const footer = this._sidebar.querySelector('.jux-sidebar-footer');
226
+ if (footer) footer.textContent = value;
227
+ }
228
+ }
229
+
230
+ /* ═════════════════════════════════════════════════════════════════
231
+ * FLUENT API
232
+ * ═════════════════════════════════════════════════════════════════ */
82
233
 
83
234
  width(value: string): this {
84
235
  this.state.width = value;
236
+ if (this._sidebar) {
237
+ this._sidebar.style.width = value;
238
+ }
85
239
  return this;
86
240
  }
87
241
 
@@ -107,42 +261,38 @@ export class Sidebar extends BaseComponent<SidebarState> {
107
261
 
108
262
  collapsed(value: boolean): this {
109
263
  this.state.collapsed = value;
110
- this._updateCollapsedState();
111
264
  return this;
112
265
  }
113
266
 
114
- menu(value: MenuOptions): this {
115
- this.state.menu = value;
267
+ items(value: SidebarItem[]): this {
268
+ this.state.items = value;
116
269
  return this;
117
270
  }
118
271
 
119
- toggle(): void {
120
- this.state.collapsed = !this.state.collapsed;
121
- this._updateCollapsedState();
122
-
123
- // Save to localStorage
124
- const storageKey = `jux-sidebar-${this._id}-collapsed`;
125
- localStorage.setItem(storageKey, String(this.state.collapsed));
272
+ addItem(item: SidebarItem): this {
273
+ this.state.items = [...this.state.items, item];
274
+ return this;
275
+ }
126
276
 
127
- // 🎯 Fire the toggle callback event
128
- this._triggerCallback('toggle', this.state.collapsed);
277
+ header(value: string): this {
278
+ this.state.header = value;
279
+ return this;
129
280
  }
130
281
 
131
- /**
132
- * Refresh menu active states (called automatically on URL changes)
133
- */
134
- refreshMenu(): this {
135
- if (this._menu && this.state.menu) {
136
- // Re-evaluate and update menu items with current active states
137
- this._menu.items(this.state.menu.items || []);
138
- }
282
+ footer(value: string): this {
283
+ this.state.footer = value;
139
284
  return this;
140
285
  }
141
286
 
142
- private _updateCollapsedState(): void {
143
- if (this._sidebar) {
144
- this._sidebar.classList.toggle('jux-sidebar-collapsed', this.state.collapsed);
145
- }
287
+ toggle(): void {
288
+ this.state.collapsed = !this.state.collapsed;
289
+ this._triggerCallback('toggle', this.state.collapsed);
290
+ }
291
+
292
+ refreshActiveStates(): this {
293
+ this._setActiveStates();
294
+ this.state.items = [...this.state.items]; // Trigger update
295
+ return this;
146
296
  }
147
297
 
148
298
  /* ═════════════════════════════════════════════════════════════════
@@ -152,30 +302,52 @@ export class Sidebar extends BaseComponent<SidebarState> {
152
302
  render(targetId?: string): this {
153
303
  const container = this._setupContainer(targetId);
154
304
 
155
- const { position, collapsed, expandOnHover, menu, style, class: className } = this.state;
305
+ const { position, collapsed, expandOnHover, items, header, footer, width, style, class: className } = this.state;
156
306
 
157
- // Build sidebar element
158
307
  const sidebar = document.createElement('aside');
308
+ sidebar.className = `jux-sidebar jux-sidebar-${position}`;
159
309
  sidebar.id = this._id;
160
- if (className) sidebar.className = className;
161
- if (style) sidebar.setAttribute('style', style);
162
310
 
163
- // Apply initial collapsed state
164
311
  if (collapsed) {
165
312
  sidebar.classList.add('jux-sidebar-collapsed');
166
313
  }
167
314
 
168
- // Menu container (append first so toggle appears at bottom)
169
- const menuContainer = document.createElement('div');
170
- menuContainer.className = 'jux-sidebar-menu';
171
- menuContainer.id = `${this._id}-menu`;
172
- sidebar.appendChild(menuContainer);
315
+ if (className) sidebar.className += ` ${className}`;
316
+ if (style) sidebar.setAttribute('style', style);
317
+ if (width) sidebar.style.width = width;
318
+
319
+ // Header
320
+ if (header) {
321
+ const headerEl = document.createElement('div');
322
+ headerEl.className = 'jux-sidebar-header';
323
+ headerEl.textContent = header;
324
+ sidebar.appendChild(headerEl);
325
+ }
326
+
327
+ // Navigation
328
+ const nav = document.createElement('nav');
329
+ nav.className = 'jux-sidebar-nav';
330
+
331
+ items.forEach(item => {
332
+ nav.appendChild(this._renderItem(item));
333
+ });
334
+
335
+ sidebar.appendChild(nav);
336
+
337
+ // Footer
338
+ if (footer) {
339
+ const footerEl = document.createElement('div');
340
+ footerEl.className = 'jux-sidebar-footer';
341
+ footerEl.textContent = footer;
342
+ sidebar.appendChild(footerEl);
343
+ }
173
344
 
174
- // Toggle button (append last so it appears at bottom)
345
+ // Toggle button
175
346
  if (this.state.collapsible || this.state.showToggle) {
176
347
  const toggleBtn = document.createElement('button');
177
348
  toggleBtn.className = 'jux-sidebar-toggle';
178
349
  toggleBtn.type = 'button';
350
+ toggleBtn.setAttribute('aria-label', 'Toggle sidebar');
179
351
 
180
352
  const toggleIcon = document.createElement('span');
181
353
  toggleIcon.className = 'jux-sidebar-toggle-icon';
@@ -189,13 +361,12 @@ export class Sidebar extends BaseComponent<SidebarState> {
189
361
 
190
362
  toggleBtn.addEventListener('click', () => {
191
363
  this.toggle();
192
- toggleBtn.classList.toggle('jux-sidebar-toggle-collapsed', this.state.collapsed);
193
364
  });
194
365
 
195
366
  sidebar.appendChild(toggleBtn);
196
367
  }
197
368
 
198
- // Handle expand on hover
369
+ // Expand on hover
199
370
  if (expandOnHover) {
200
371
  sidebar.addEventListener('mouseenter', () => {
201
372
  if (this.state.collapsed) {
@@ -210,56 +381,12 @@ export class Sidebar extends BaseComponent<SidebarState> {
210
381
  });
211
382
  }
212
383
 
213
- // Wire events using inherited method
214
384
  this._wireStandardEvents(sidebar);
215
-
216
- // Wire sync bindings for 'collapsed' property
217
- this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
218
- if (property === 'collapsed') {
219
- const transform = toComponent || ((v: any) => v);
220
-
221
- stateObj.subscribe((val: any) => {
222
- const transformed = transform(val);
223
- const isCollapsed = Boolean(transformed);
224
- this.state.collapsed = isCollapsed;
225
- this._updateCollapsedState();
226
- });
227
- }
228
- else if (property === 'menu') {
229
- const transform = toComponent || ((v: any) => v);
230
-
231
- stateObj.subscribe((val: any) => {
232
- const transformed = transform(val);
233
- this.state.menu = transformed;
234
-
235
- // Update menu items if menu instance exists
236
- if (this._menu) {
237
- this._menu.items(transformed.items || []);
238
-
239
- // Re-render the menu
240
- const menuEl = menuContainer.querySelector(`#${this._id}-menu-instance`);
241
- if (menuEl) {
242
- menuEl.remove();
243
- }
244
- this._menu.render(`#${menuContainer.id}`);
245
- }
246
- });
247
- }
248
- });
385
+ this._wireAllSyncs();
249
386
 
250
387
  container.appendChild(sidebar);
251
388
  this._sidebar = sidebar;
252
389
 
253
- // Create and render menu (after sidebar is in DOM)
254
- if (menu) {
255
- this._menu = new Menu(`${this._id}-menu-instance`, {
256
- ...menu,
257
- orientation: 'vertical'
258
- });
259
- this._menu.render(`#${menuContainer.id}`);
260
- }
261
-
262
- // Trigger Iconify icon rendering
263
390
  requestAnimationFrame(() => {
264
391
  if ((window as any).Iconify) {
265
392
  (window as any).Iconify.scan();
@@ -268,10 +395,6 @@ export class Sidebar extends BaseComponent<SidebarState> {
268
395
 
269
396
  return this;
270
397
  }
271
-
272
- update(prop: string, value: any): void {
273
- // No reactive updates needed
274
- }
275
398
  }
276
399
 
277
400
  export function sidebar(id: string, options: SidebarOptions = {}): Sidebar {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.14",
3
+ "version": "1.1.15",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "index.js",
@@ -1,42 +0,0 @@
1
- import { BaseComponent } from './base/BaseComponent.js';
2
- import { State } from '../reactivity/state.js';
3
- declare global {
4
- interface Window {
5
- juxBeforeNavigate?: (from: string, to: string) => boolean | string;
6
- juxAfterNavigate?: (path: string) => void;
7
- }
8
- }
9
- export interface GuardOptions {
10
- authState?: State<boolean>;
11
- loginPath?: string;
12
- protectedPaths?: string[];
13
- }
14
- type GuardState = {
15
- authState: State<boolean> | null;
16
- loginPath: string;
17
- protectedPaths: string[];
18
- isActive: boolean;
19
- };
20
- /**
21
- * ⚠️ DEPRECATED: Guard component is no longer supported after removing global middleware.
22
- *
23
- * Recommended alternatives:
24
- * 1. Server-side authentication (Express, FastAPI, Laravel)
25
- * 2. Manual route checks in each view
26
- * 3. Custom wrapper components
27
- *
28
- * This component will be removed in a future version.
29
- */
30
- export declare class Guard extends BaseComponent<GuardState> {
31
- constructor(id: string, options?: GuardOptions);
32
- protected getTriggerEvents(): readonly string[];
33
- protected getCallbackEvents(): readonly string[];
34
- requireAuth(authState: State<boolean>, loginPath?: string): this;
35
- protect(...paths: string[]): this;
36
- render(targetId?: string): this;
37
- deactivate(): this;
38
- update(prop: string, value: any): void;
39
- }
40
- export declare function guard(id: string, options?: GuardOptions): Guard;
41
- export {};
42
- //# sourceMappingURL=guard.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAG/C,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,MAAM;QACZ,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,MAAM,CAAC;QACnE,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;KAC7C;CACJ;AAMD,MAAM,WAAW,YAAY;IACzB,SAAS,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,KAAK,UAAU,GAAG;IACd,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF;;;;;;;;;GASG;AACH,qBAAa,KAAM,SAAQ,aAAa,CAAC,UAAU,CAAC;gBACpC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB;IAclD,SAAS,CAAC,gBAAgB,IAAI,SAAS,MAAM,EAAE;IAI/C,SAAS,CAAC,iBAAiB,IAAI,SAAS,MAAM,EAAE;IAQhD,WAAW,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAKhE,OAAO,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IASjC,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAK/B,UAAU,IAAI,IAAI;IAIlB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;CAGzC;AAED,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,KAAK,CAE/D"}
@@ -1,59 +0,0 @@
1
- import { BaseComponent } from './base/BaseComponent.js';
2
- // Event definitions
3
- const TRIGGER_EVENTS = [];
4
- const CALLBACK_EVENTS = ['blocked', 'allowed'];
5
- /**
6
- * ⚠️ DEPRECATED: Guard component is no longer supported after removing global middleware.
7
- *
8
- * Recommended alternatives:
9
- * 1. Server-side authentication (Express, FastAPI, Laravel)
10
- * 2. Manual route checks in each view
11
- * 3. Custom wrapper components
12
- *
13
- * This component will be removed in a future version.
14
- */
15
- export class Guard extends BaseComponent {
16
- constructor(id, options = {}) {
17
- super(id, {
18
- authState: options.authState ?? null,
19
- loginPath: options.loginPath ?? '/login',
20
- protectedPaths: options.protectedPaths ?? [],
21
- isActive: false
22
- });
23
- console.warn('[Jux Guard] DEPRECATED: Guard component no longer supported after middleware removal.\n' +
24
- 'Use server-side auth or manual checks instead.');
25
- }
26
- getTriggerEvents() {
27
- return TRIGGER_EVENTS;
28
- }
29
- getCallbackEvents() {
30
- return CALLBACK_EVENTS;
31
- }
32
- /* ═════════════════════════════════════════════════════════════════
33
- * FLUENT API (No-ops now)
34
- * ═════════════════════════════════════════════════════════════════ */
35
- requireAuth(authState, loginPath) {
36
- console.warn('[Jux Guard] DEPRECATED: requireAuth() has no effect');
37
- return this;
38
- }
39
- protect(...paths) {
40
- console.warn('[Jux Guard] DEPRECATED: protect() has no effect');
41
- return this;
42
- }
43
- /* ═════════════════════════════════════════════════════════════════
44
- * RENDER (No-op)
45
- * ═════════════════════════════════════════════════════════════════ */
46
- render(targetId) {
47
- console.warn('[Jux Guard] DEPRECATED: Guard rendering has no effect');
48
- return this;
49
- }
50
- deactivate() {
51
- return this;
52
- }
53
- update(prop, value) {
54
- // No reactive updates needed
55
- }
56
- }
57
- export function guard(id, options) {
58
- return new Guard(id, options);
59
- }
@@ -1,96 +0,0 @@
1
- import { BaseComponent } from './base/BaseComponent.js';
2
- import { State } from '../reactivity/state.js';
3
-
4
- // Extend Window interface to include Jux navigation hooks
5
- declare global {
6
- interface Window {
7
- juxBeforeNavigate?: (from: string, to: string) => boolean | string;
8
- juxAfterNavigate?: (path: string) => void;
9
- }
10
- }
11
-
12
- // Event definitions
13
- const TRIGGER_EVENTS = [] as const;
14
- const CALLBACK_EVENTS = ['blocked', 'allowed'] as const;
15
-
16
- export interface GuardOptions {
17
- authState?: State<boolean>; // Check if user is authenticated
18
- loginPath?: string; // Where to redirect if blocked
19
- protectedPaths?: string[]; // Paths that require auth
20
- }
21
-
22
- type GuardState = {
23
- authState: State<boolean> | null;
24
- loginPath: string;
25
- protectedPaths: string[];
26
- isActive: boolean;
27
- };
28
-
29
- /**
30
- * ⚠️ DEPRECATED: Guard component is no longer supported after removing global middleware.
31
- *
32
- * Recommended alternatives:
33
- * 1. Server-side authentication (Express, FastAPI, Laravel)
34
- * 2. Manual route checks in each view
35
- * 3. Custom wrapper components
36
- *
37
- * This component will be removed in a future version.
38
- */
39
- export class Guard extends BaseComponent<GuardState> {
40
- constructor(id: string, options: GuardOptions = {}) {
41
- super(id, {
42
- authState: options.authState ?? null,
43
- loginPath: options.loginPath ?? '/login',
44
- protectedPaths: options.protectedPaths ?? [],
45
- isActive: false
46
- });
47
-
48
- console.warn(
49
- '[Jux Guard] DEPRECATED: Guard component no longer supported after middleware removal.\n' +
50
- 'Use server-side auth or manual checks instead.'
51
- );
52
- }
53
-
54
- protected getTriggerEvents(): readonly string[] {
55
- return TRIGGER_EVENTS;
56
- }
57
-
58
- protected getCallbackEvents(): readonly string[] {
59
- return CALLBACK_EVENTS;
60
- }
61
-
62
- /* ═════════════════════════════════════════════════════════════════
63
- * FLUENT API (No-ops now)
64
- * ═════════════════════════════════════════════════════════════════ */
65
-
66
- requireAuth(authState: State<boolean>, loginPath?: string): this {
67
- console.warn('[Jux Guard] DEPRECATED: requireAuth() has no effect');
68
- return this;
69
- }
70
-
71
- protect(...paths: string[]): this {
72
- console.warn('[Jux Guard] DEPRECATED: protect() has no effect');
73
- return this;
74
- }
75
-
76
- /* ═════════════════════════════════════════════════════════════════
77
- * RENDER (No-op)
78
- * ═════════════════════════════════════════════════════════════════ */
79
-
80
- render(targetId?: string): this {
81
- console.warn('[Jux Guard] DEPRECATED: Guard rendering has no effect');
82
- return this;
83
- }
84
-
85
- deactivate(): this {
86
- return this;
87
- }
88
-
89
- update(prop: string, value: any): void {
90
- // No reactive updates needed
91
- }
92
- }
93
-
94
- export function guard(id: string, options?: GuardOptions): Guard {
95
- return new Guard(id, options);
96
- }