juxscript 1.0.20 → 1.0.21

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 (76) hide show
  1. package/bin/cli.js +121 -72
  2. package/lib/components/alert.ts +143 -92
  3. package/lib/components/badge.ts +93 -94
  4. package/lib/components/base/BaseComponent.ts +397 -0
  5. package/lib/components/base/FormInput.ts +322 -0
  6. package/lib/components/button.ts +40 -131
  7. package/lib/components/card.ts +57 -79
  8. package/lib/components/charts/areachart.ts +315 -0
  9. package/lib/components/charts/barchart.ts +421 -0
  10. package/lib/components/charts/doughnutchart.ts +263 -0
  11. package/lib/components/charts/lib/BaseChart.ts +402 -0
  12. package/lib/components/{chart-types.ts → charts/lib/chart-types.ts} +1 -1
  13. package/lib/components/{chart-utils.ts → charts/lib/chart-utils.ts} +1 -1
  14. package/lib/components/{chart.ts → charts/lib/chart.ts} +3 -3
  15. package/lib/components/checkbox.ts +255 -204
  16. package/lib/components/code.ts +31 -78
  17. package/lib/components/container.ts +113 -130
  18. package/lib/components/data.ts +37 -5
  19. package/lib/components/datepicker.ts +180 -147
  20. package/lib/components/dialog.ts +218 -221
  21. package/lib/components/divider.ts +63 -87
  22. package/lib/components/docs-data.json +498 -2404
  23. package/lib/components/dropdown.ts +191 -236
  24. package/lib/components/element.ts +196 -145
  25. package/lib/components/fileupload.ts +253 -167
  26. package/lib/components/guard.ts +92 -0
  27. package/lib/components/heading.ts +31 -97
  28. package/lib/components/helpers.ts +13 -6
  29. package/lib/components/hero.ts +51 -114
  30. package/lib/components/icon.ts +33 -120
  31. package/lib/components/icons.ts +2 -1
  32. package/lib/components/include.ts +76 -3
  33. package/lib/components/input.ts +155 -407
  34. package/lib/components/kpicard.ts +16 -16
  35. package/lib/components/list.ts +358 -261
  36. package/lib/components/loading.ts +142 -211
  37. package/lib/components/menu.ts +63 -152
  38. package/lib/components/modal.ts +42 -129
  39. package/lib/components/nav.ts +79 -101
  40. package/lib/components/paragraph.ts +38 -102
  41. package/lib/components/progress.ts +108 -166
  42. package/lib/components/radio.ts +283 -234
  43. package/lib/components/script.ts +19 -87
  44. package/lib/components/select.ts +189 -199
  45. package/lib/components/sidebar.ts +110 -141
  46. package/lib/components/style.ts +19 -82
  47. package/lib/components/switch.ts +254 -183
  48. package/lib/components/table.ts +1078 -208
  49. package/lib/components/tabs.ts +42 -106
  50. package/lib/components/theme-toggle.ts +73 -165
  51. package/lib/components/tooltip.ts +85 -316
  52. package/lib/components/write.ts +108 -127
  53. package/lib/jux.ts +67 -41
  54. package/machinery/build.js +466 -0
  55. package/machinery/compiler.js +354 -105
  56. package/machinery/server.js +23 -100
  57. package/machinery/watcher.js +153 -130
  58. package/package.json +1 -1
  59. package/presets/base.css +1166 -0
  60. package/presets/notion.css +2 -1975
  61. package/lib/adapters/base-adapter.js +0 -35
  62. package/lib/adapters/index.js +0 -33
  63. package/lib/adapters/mysql-adapter.js +0 -65
  64. package/lib/adapters/postgres-adapter.js +0 -70
  65. package/lib/adapters/sqlite-adapter.js +0 -56
  66. package/lib/components/areachart.ts +0 -1128
  67. package/lib/components/areachartsmooth.ts +0 -1380
  68. package/lib/components/barchart.ts +0 -1322
  69. package/lib/components/doughnutchart.ts +0 -1259
  70. package/lib/components/footer.ts +0 -165
  71. package/lib/components/header.ts +0 -187
  72. package/lib/components/layout.ts +0 -239
  73. package/lib/components/main.ts +0 -137
  74. package/lib/layouts/default.jux +0 -8
  75. package/lib/layouts/figma.jux +0 -0
  76. /package/lib/{themes → components/charts/lib}/charts.js +0 -0
@@ -1,5 +1,9 @@
1
- import { getOrCreateContainer } from './helpers.js';
2
- import { State } from '../reactivity/state.js';
1
+ import { FormInput, FormInputState } from './base/FormInput.js';
2
+ import { renderIcon } from './icons.js';
3
+
4
+ // Event definitions
5
+ const TRIGGER_EVENTS = [] as const;
6
+ const CALLBACK_EVENTS = ['change', 'filesSelected', 'clear'] as const;
3
7
 
4
8
  export interface FileUploadOptions {
5
9
  label?: string;
@@ -7,61 +11,62 @@ export interface FileUploadOptions {
7
11
  multiple?: boolean;
8
12
  disabled?: boolean;
9
13
  name?: string;
14
+ icon?: string;
15
+ required?: boolean;
10
16
  style?: string;
11
17
  class?: string;
18
+ onValidate?: (files: File[]) => boolean | string;
12
19
  }
13
20
 
14
- type FileUploadState = {
21
+ interface FileUploadState extends FormInputState {
15
22
  files: File[];
16
- label: string;
17
23
  accept: string;
18
24
  multiple: boolean;
19
- disabled: boolean;
20
- name: string;
21
- style: string;
22
- class: string;
23
- };
24
-
25
- export class FileUpload {
26
- state: FileUploadState;
27
- container: HTMLElement | null = null;
28
- _id: string;
29
- id: string;
30
-
31
- // CRITICAL: Store bind/sync instructions for deferred wiring
32
- private _bindings: Array<{ event: string, handler: Function }> = [];
33
- private _syncBindings: Array<{
34
- property: string,
35
- stateObj: State<any>,
36
- toState?: Function,
37
- toComponent?: Function
38
- }> = [];
25
+ icon: string;
26
+ }
39
27
 
40
- constructor(id: string, options: FileUploadOptions = {}) {
41
- this._id = id;
42
- this.id = id;
28
+ export class FileUpload extends FormInput<FileUploadState> {
29
+ private _fileListElement: HTMLElement | null = null;
43
30
 
44
- this.state = {
31
+ constructor(id: string, options: FileUploadOptions = {}) {
32
+ super(id, {
45
33
  files: [],
46
- label: options.label ?? '',
47
34
  accept: options.accept ?? '',
48
35
  multiple: options.multiple ?? false,
36
+ icon: options.icon ?? 'upload',
37
+ label: options.label ?? '',
38
+ required: options.required ?? false,
49
39
  disabled: options.disabled ?? false,
50
40
  name: options.name ?? id,
51
41
  style: options.style ?? '',
52
- class: options.class ?? ''
53
- };
42
+ class: options.class ?? '',
43
+ errorMessage: undefined
44
+ });
45
+
46
+ if (options.onValidate) {
47
+ this._onValidate = options.onValidate;
48
+ }
54
49
  }
55
50
 
56
- /* -------------------------
57
- * Fluent API
58
- * ------------------------- */
51
+ protected getTriggerEvents(): readonly string[] {
52
+ return TRIGGER_EVENTS;
53
+ }
59
54
 
60
- label(value: string): this {
61
- this.state.label = value;
62
- return this;
55
+ protected getCallbackEvents(): readonly string[] {
56
+ return CALLBACK_EVENTS;
63
57
  }
64
58
 
59
+ /* ═════════════════════════════════════════════════════════════════
60
+ * FLUENT API
61
+ * ═════════════════════════════════════════════════════════════════ */
62
+
63
+ // ✅ Inherited from FormInput/BaseComponent:
64
+ // - label(), required(), name(), onValidate()
65
+ // - validate(), isValid()
66
+ // - style(), class()
67
+ // - bind(), sync(), renderTo()
68
+ // - disabled(), enable(), disable()
69
+
65
70
  accept(value: string): this {
66
71
  this.state.accept = value;
67
72
  return this;
@@ -72,194 +77,224 @@ export class FileUpload {
72
77
  return this;
73
78
  }
74
79
 
75
- disabled(value: boolean): this {
76
- this.state.disabled = value;
77
- this._updateElement();
80
+ icon(value: string): this {
81
+ this.state.icon = value;
78
82
  return this;
79
83
  }
80
84
 
81
- name(value: string): this {
82
- this.state.name = value;
85
+ clear(): this {
86
+ this.state.files = [];
87
+ if (this._inputElement) {
88
+ (this._inputElement as HTMLInputElement).value = '';
89
+ }
90
+ if (this._fileListElement) {
91
+ this._updateFileList([]);
92
+ }
93
+ // 🎯 Fire the clear callback event
94
+ this._triggerCallback('clear');
83
95
  return this;
84
96
  }
85
97
 
86
- style(value: string): this {
87
- this.state.style = value;
88
- return this;
89
- }
98
+ /* ═════════════════════════════════════════════════════════════════
99
+ * FORM INPUT IMPLEMENTATION
100
+ * ═════════════════════════════════════════════════════════════════ */
90
101
 
91
- class(value: string): this {
92
- this.state.class = value;
93
- return this;
94
- }
95
-
96
- bind(event: string, handler: Function): this {
97
- this._bindings.push({ event, handler });
98
- return this;
102
+ getValue(): File[] {
103
+ return this.state.files;
99
104
  }
100
105
 
101
- sync(property: string, stateObj: State<any>, toState?: Function, toComponent?: Function): this {
102
- if (!stateObj || typeof stateObj.subscribe !== 'function') {
103
- throw new Error(`FileUpload.sync: Expected a State object for property "${property}"`);
106
+ setValue(files: File[]): this {
107
+ this.state.files = files;
108
+ if (this._fileListElement) {
109
+ this._updateFileList(files);
104
110
  }
105
- this._syncBindings.push({ property, stateObj, toState, toComponent });
106
111
  return this;
107
112
  }
108
113
 
109
- /* -------------------------
110
- * Helpers
111
- * ------------------------- */
114
+ getFiles(): File[] {
115
+ return this.getValue();
116
+ }
112
117
 
113
- private _updateElement(): void {
114
- const input = document.getElementById(`${this._id}-input`) as HTMLInputElement;
115
- const button = document.getElementById(`${this._id}-button`) as HTMLButtonElement;
118
+ protected _validateValue(files: File[]): boolean | string {
119
+ const { required } = this.state;
116
120
 
117
- if (input) {
118
- input.disabled = this.state.disabled;
121
+ if (required && files.length === 0) {
122
+ return 'Please select at least one file';
119
123
  }
120
- if (button) {
121
- button.disabled = this.state.disabled;
124
+
125
+ if (this._onValidate) {
126
+ const result = this._onValidate(files);
127
+ if (result !== true) {
128
+ return result || 'Invalid files';
129
+ }
122
130
  }
123
- }
124
131
 
125
- getFiles(): File[] {
126
- return this.state.files;
132
+ return true;
127
133
  }
128
134
 
129
- clear(): void {
130
- this.state.files = [];
131
- const input = document.getElementById(`${this._id}-input`) as HTMLInputElement;
132
- const fileList = document.getElementById(`${this._id}-list`);
135
+ protected _buildInputElement(): HTMLElement {
136
+ const { accept, multiple, required, disabled, name } = this.state;
133
137
 
134
- if (input) {
135
- input.value = '';
136
- }
137
- if (fileList) {
138
- this._updateFileList(fileList, []);
139
- }
138
+ const input = document.createElement('input');
139
+ input.type = 'file';
140
+ input.className = 'jux-fileupload-input';
141
+ input.id = `${this._id}-input`;
142
+ input.name = name;
143
+ input.required = required;
144
+ input.disabled = disabled;
145
+
146
+ if (accept) input.accept = accept;
147
+ if (multiple) input.multiple = multiple;
148
+
149
+ return input;
140
150
  }
141
151
 
142
- /* -------------------------
143
- * Render (5-Step Pattern)
144
- * ------------------------- */
152
+ /* ═════════════════════════════════════════════════════════════════
153
+ * RENDER
154
+ * ═════════════════════════════════════════════════════════════════ */
145
155
 
146
156
  render(targetId?: string): this {
147
- // === 1. SETUP: Get or create container ===
148
- let container: HTMLElement;
149
- if (targetId) {
150
- const target = document.querySelector(targetId);
151
- if (!target || !(target instanceof HTMLElement)) {
152
- throw new Error(`FileUpload: Target "${targetId}" not found`);
153
- }
154
- container = target;
155
- } else {
156
- container = getOrCreateContainer(this._id);
157
- }
158
- this.container = container;
157
+ const container = this._setupContainer(targetId);
159
158
 
160
- // === 2. PREPARE: Destructure state and check sync flags ===
161
- const { label, accept, multiple, disabled, name, style, class: className } = this.state;
162
- const hasFilesSync = this._syncBindings.some(b => b.property === 'files');
159
+ const { icon, style, class: className } = this.state;
163
160
 
164
- // === 3. BUILD: Create DOM elements ===
161
+ // Build wrapper
165
162
  const wrapper = document.createElement('div');
166
- wrapper.className = 'jux-fileupload';
163
+ wrapper.className = 'jux-input jux-fileupload';
167
164
  wrapper.id = this._id;
168
165
  if (className) wrapper.className += ` ${className}`;
169
166
  if (style) wrapper.setAttribute('style', style);
170
167
 
171
- if (label) {
172
- const labelEl = document.createElement('label');
173
- labelEl.className = 'jux-fileupload-label';
174
- labelEl.textContent = label;
175
- wrapper.appendChild(labelEl);
168
+ // Label
169
+ if (this.state.label) {
170
+ wrapper.appendChild(this._renderLabel());
176
171
  }
177
172
 
178
- const input = document.createElement('input');
179
- input.type = 'file';
180
- input.className = 'jux-fileupload-input';
181
- input.id = `${this._id}-input`;
182
- input.name = name;
183
- input.disabled = disabled;
184
- if (accept) input.accept = accept;
185
- if (multiple) input.multiple = multiple;
173
+ // Hidden file input
174
+ const inputEl = this._buildInputElement() as HTMLInputElement;
175
+ this._inputElement = inputEl;
176
+ wrapper.appendChild(inputEl);
177
+
178
+ // Button container
179
+ const buttonContainer = document.createElement('div');
180
+ buttonContainer.className = 'jux-fileupload-button-container';
181
+
182
+ if (icon) {
183
+ const iconEl = document.createElement('span');
184
+ iconEl.className = 'jux-fileupload-icon';
185
+ iconEl.appendChild(renderIcon(icon));
186
+ buttonContainer.appendChild(iconEl);
187
+ }
186
188
 
187
189
  const button = document.createElement('button');
188
190
  button.type = 'button';
189
191
  button.className = 'jux-fileupload-button';
190
- button.id = `${this._id}-button`;
191
192
  button.textContent = 'Choose File(s)';
192
- button.disabled = disabled;
193
+ button.disabled = this.state.disabled;
193
194
 
195
+ buttonContainer.appendChild(button);
196
+ wrapper.appendChild(buttonContainer);
197
+
198
+ // File list
194
199
  const fileList = document.createElement('div');
195
200
  fileList.className = 'jux-fileupload-list';
196
- fileList.id = `${this._id}-list`;
197
-
198
- wrapper.appendChild(button);
199
- wrapper.appendChild(input);
201
+ this._fileListElement = fileList;
200
202
  wrapper.appendChild(fileList);
201
203
 
202
- // === 4. WIRE: Attach event listeners and sync bindings ===
204
+ // Error element
205
+ wrapper.appendChild(this._renderError());
203
206
 
204
207
  // Button click triggers file input
205
- button.addEventListener('click', () => input.click());
208
+ button.addEventListener('click', () => inputEl.click());
206
209
 
207
- // Default file change behavior (only if NOT using sync)
208
- if (!hasFilesSync) {
209
- input.addEventListener('change', () => {
210
- this.state.files = Array.from(input.files || []);
211
- this._updateFileList(fileList, this.state.files);
210
+ // Wire events
211
+ this._wireStandardEvents(wrapper);
212
+
213
+ // Wire file-specific sync
214
+ const filesSync = this._syncBindings.find(b => b.property === 'files' || b.property === 'value');
215
+
216
+ if (filesSync) {
217
+ const { stateObj, toState, toComponent } = filesSync;
218
+
219
+ const transformToState = toState || ((v: File[]) => v);
220
+ const transformToComponent = toComponent || ((v: any) => v);
221
+
222
+ let isUpdating = false;
223
+
224
+ // State → Component
225
+ stateObj.subscribe((val: any) => {
226
+ if (isUpdating) return;
227
+ const transformed = transformToComponent(val);
228
+ this.setValue(transformed);
212
229
  });
213
- }
214
230
 
215
- // Wire custom bindings from .bind() calls
216
- this._bindings.forEach(({ event, handler }) => {
217
- wrapper.addEventListener(event, handler as EventListener);
218
- });
231
+ // Component State
232
+ inputEl.addEventListener('change', () => {
233
+ if (isUpdating) return;
234
+ isUpdating = true;
219
235
 
220
- // Wire sync bindings from .sync() calls
221
- this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
222
- if (property === 'files') {
223
- const transformToState = toState || ((v: any) => v);
224
- const transformToComponent = toComponent || ((v: any) => v);
236
+ const files = Array.from(inputEl.files || []);
237
+ this.state.files = files;
238
+ this._updateFileList(files);
239
+ this._clearError();
225
240
 
226
- let isUpdating = false;
241
+ const transformed = transformToState(files);
242
+ stateObj.set(transformed);
227
243
 
228
- // State Component
229
- stateObj.subscribe((val: any) => {
230
- if (isUpdating) return;
231
- const transformed = transformToComponent(val);
232
- this.state.files = transformed;
233
- this._updateFileList(fileList, transformed);
234
- });
244
+ // 🎯 Fire the callback events
245
+ this._triggerCallback('change', files);
246
+ this._triggerCallback('filesSelected', files);
235
247
 
236
- // Component State
237
- input.addEventListener('change', () => {
238
- if (isUpdating) return;
239
- isUpdating = true;
248
+ setTimeout(() => { isUpdating = false; }, 0);
249
+ });
250
+ } else {
251
+ // Default behavior without sync
252
+ inputEl.addEventListener('change', () => {
253
+ const files = Array.from(inputEl.files || []);
254
+ this.state.files = files;
255
+ this._updateFileList(files);
256
+ this._clearError();
257
+
258
+ // 🎯 Fire the callback events
259
+ this._triggerCallback('change', files);
260
+ this._triggerCallback('filesSelected', files);
261
+ });
262
+ }
240
263
 
241
- const files = Array.from(input.files || []);
242
- this.state.files = files;
243
- this._updateFileList(fileList, files);
264
+ // Always add blur validation
265
+ inputEl.addEventListener('blur', () => {
266
+ this.validate();
267
+ });
244
268
 
245
- const transformed = transformToState(files);
246
- stateObj.set(transformed);
269
+ // Sync label changes
270
+ const labelSync = this._syncBindings.find(b => b.property === 'label');
271
+ if (labelSync) {
272
+ const transform = labelSync.toComponent || ((v: any) => String(v));
273
+ labelSync.stateObj.subscribe((val: any) => {
274
+ this.label(transform(val));
275
+ });
276
+ }
247
277
 
248
- setTimeout(() => { isUpdating = false; }, 0);
249
- });
278
+ container.appendChild(wrapper);
279
+ this._injectFileUploadStyles();
280
+ this._injectFormStyles();
281
+
282
+ requestAnimationFrame(() => {
283
+ if ((window as any).lucide) {
284
+ (window as any).lucide.createIcons();
250
285
  }
251
286
  });
252
287
 
253
- // === 5. RENDER: Append to DOM and finalize ===
254
- container.appendChild(wrapper);
255
288
  return this;
256
289
  }
257
290
 
258
- private _updateFileList(fileList: HTMLElement, files: File[]): void {
259
- fileList.innerHTML = '';
291
+ private _updateFileList(files: File[]): void {
292
+ if (!this._fileListElement) return;
293
+
294
+ this._fileListElement.innerHTML = '';
260
295
 
261
296
  if (files.length === 0) {
262
- fileList.textContent = 'No files selected';
297
+ this._fileListElement.textContent = 'No files selected';
263
298
  return;
264
299
  }
265
300
 
@@ -267,7 +302,7 @@ export class FileUpload {
267
302
  const fileItem = document.createElement('div');
268
303
  fileItem.className = 'jux-fileupload-item';
269
304
  fileItem.textContent = `${file.name} (${this._formatFileSize(file.size)})`;
270
- fileList.appendChild(fileItem);
305
+ this._fileListElement!.appendChild(fileItem);
271
306
  });
272
307
  }
273
308
 
@@ -277,11 +312,62 @@ export class FileUpload {
277
312
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
278
313
  }
279
314
 
280
- renderTo(juxComponent: any): this {
281
- if (!juxComponent?._id) {
282
- throw new Error('FileUpload.renderTo: Invalid component');
283
- }
284
- return this.render(`#${juxComponent._id}`);
315
+ private _injectFileUploadStyles(): void {
316
+ const styleId = 'jux-fileupload-styles';
317
+ if (document.getElementById(styleId)) return;
318
+
319
+ const style = document.createElement('style');
320
+ style.id = styleId;
321
+ style.textContent = `
322
+ .jux-fileupload-input {
323
+ display: none;
324
+ }
325
+
326
+ .jux-fileupload-button-container {
327
+ display: inline-flex;
328
+ align-items: center;
329
+ gap: 8px;
330
+ }
331
+
332
+ .jux-fileupload-icon svg {
333
+ width: 18px;
334
+ height: 18px;
335
+ }
336
+
337
+ .jux-fileupload-button {
338
+ padding: 8px 16px;
339
+ background: #3b82f6;
340
+ color: white;
341
+ border: none;
342
+ border-radius: 6px;
343
+ font-size: 14px;
344
+ cursor: pointer;
345
+ transition: background 0.2s;
346
+ }
347
+
348
+ .jux-fileupload-button:hover:not(:disabled) {
349
+ background: #2563eb;
350
+ }
351
+
352
+ .jux-fileupload-button:disabled {
353
+ background: #9ca3af;
354
+ cursor: not-allowed;
355
+ }
356
+
357
+ .jux-fileupload-list {
358
+ margin-top: 12px;
359
+ font-size: 14px;
360
+ color: #6b7280;
361
+ }
362
+
363
+ .jux-fileupload-item {
364
+ padding: 8px;
365
+ background: #f3f4f6;
366
+ border-radius: 4px;
367
+ margin-bottom: 4px;
368
+ }
369
+ `;
370
+ document.head.appendChild(style);
285
371
  }
286
372
  }
287
373
 
@@ -0,0 +1,92 @@
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
+
90
+ export function guard(id: string, options?: GuardOptions): Guard {
91
+ return new Guard(id, options);
92
+ }