juxscript 1.1.69 → 1.1.71

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,210 +1,61 @@
1
-
2
1
  /**
3
- * Include - Include external resources (CSS, JS, images, fonts, etc.)
4
- * Auto-detects resource type from URL and provides simple, fluent API
2
+ * Include - Simplified resource injection for bundled and external resources
3
+ * Supports page-specific scoping and cleanup
5
4
  */
6
5
 
7
- type IncludeType = 'stylesheet' | 'script' | 'image' | 'font' | 'preload' | 'prefetch' | 'module' | 'json';
8
- type IncludeLocation = 'head' | 'body-start' | 'body-end';
6
+ type ResourceType = 'css' | 'js' | 'module';
9
7
 
10
8
  interface IncludeOptions {
11
- as?: string;
12
- crossOrigin?: 'anonymous' | 'use-credentials';
13
- integrity?: string;
9
+ type?: ResourceType;
10
+ target?: string; // CSS selector for target container (default: 'head')
14
11
  async?: boolean;
15
12
  defer?: boolean;
16
- location?: IncludeLocation;
13
+ crossOrigin?: 'anonymous' | 'use-credentials';
14
+ integrity?: string;
15
+ pageScoped?: boolean; // If true, tracks for cleanup
17
16
  }
18
17
 
18
+ // Global registry for page-scoped resources
19
+ const scopedResources: Map<string, Set<HTMLElement>> = new Map();
20
+
19
21
  export class Include {
20
22
  private url: string;
21
- private type: IncludeType;
22
- private options: IncludeOptions = {};
23
+ private options: IncludeOptions;
23
24
  private element: HTMLElement | null = null;
24
- private explicitType: boolean = false; // NEW: Track if type was explicitly set
25
-
26
- constructor(urlOrFile: string) {
27
- this.url = urlOrFile;
28
- this.type = this.detectType(urlOrFile);
29
- }
30
-
31
- /* -------------------------
32
- * Type Detection
33
- * ------------------------- */
34
-
35
- private detectType(url: string): IncludeType {
36
- // Check for common script patterns in URLs (CDN, etc.)
37
- if (url.includes('tailwindcss') ||
38
- url.includes('jsdelivr.net/npm') ||
39
- url.includes('unpkg.com') ||
40
- url.includes('cdn.') ||
41
- url.match(/\.(js|mjs)($|\?)/)) {
42
- return 'script';
43
- }
44
-
45
- if (url.endsWith('.css')) return 'stylesheet';
46
- if (url.endsWith('.json')) return 'json';
47
- if (url.match(/\.(png|jpg|jpeg|gif|svg|webp)$/i)) return 'image';
48
- if (url.match(/\.(woff|woff2|ttf|otf|eot)$/i)) return 'font';
49
-
50
- // Default to script for extensionless URLs from CDN domains
51
- if (!url.includes('.') || url.match(/^https?:\/\/cdn/)) {
52
- return 'script';
53
- }
54
-
55
- return 'preload';
56
- }
57
-
58
- /* -------------------------
59
- * Fluent Type Setters
60
- * ------------------------- */
61
-
62
- /**
63
- * Force treat as CSS stylesheet
64
- * Use when URL doesn't have .css extension
65
- */
66
- withCss(): this {
67
- this.type = 'stylesheet';
68
- this.explicitType = true; // Mark as explicit
69
- return this;
70
- }
71
-
72
- /**
73
- * Force treat as JavaScript
74
- * Use when URL doesn't have .js extension (CDN scripts, etc.)
75
- *
76
- * @example
77
- * jux.include('https://cdn.tailwindcss.com').withJs().render();
78
- * jux.include('https://unpkg.com/alpine').withJs({ defer: true }).render();
79
- */
80
- withJs(options?: { async?: boolean; defer?: boolean }): this {
81
- this.type = 'script';
82
- this.explicitType = true; // Mark as explicit
83
- if (options?.async) this.options.async = true;
84
- if (options?.defer) this.options.defer = true;
85
- return this;
86
- }
87
-
88
- /**
89
- * Force treat as ES module
90
- * Use for module scripts
91
- */
92
- withModule(): this {
93
- this.type = 'module';
94
- this.explicitType = true; // Mark as explicit
95
- return this;
96
- }
97
-
98
- withImage(): this {
99
- this.type = 'image';
100
- this.explicitType = true;
101
- return this;
102
- }
103
-
104
- withFont(): this {
105
- this.type = 'font';
106
- this.explicitType = true;
107
- return this;
108
- }
109
-
110
- withPreload(as?: string): this {
111
- this.type = 'preload';
112
- this.explicitType = true;
113
- if (as) this.options.as = as;
114
- return this;
115
- }
116
-
117
- withPrefetch(): this {
118
- this.type = 'prefetch';
119
- this.explicitType = true;
120
- return this;
121
- }
122
-
123
- withJson(): this {
124
- this.type = 'json';
125
- this.explicitType = true;
126
- return this;
127
- }
128
-
129
- /* -------------------------
130
- * Convenience aliases for common patterns
131
- * ------------------------- */
132
-
133
- /**
134
- * Shorthand for .withJs()
135
- * @example jux.include(url).asScript()
136
- */
137
- asScript(options?: { async?: boolean; defer?: boolean }): this {
138
- return this.withJs(options);
139
- }
140
-
141
- /**
142
- * Shorthand for .withCss()
143
- * @example jux.include(url).asStylesheet()
144
- */
145
- asStylesheet(): this {
146
- return this.withCss();
147
- }
148
-
149
- /* -------------------------
150
- * JSON Fetching
151
- * ------------------------- */
152
-
153
- /**
154
- * Fetch and parse JSON file
155
- * Returns a Promise that resolves to the parsed JSON data
156
- *
157
- * Usage:
158
- * const config = await jux.include('config.json').asJson();
159
- * const data = await jux.include('/api/data').asJson();
160
- */
161
- async asJson<T = any>(): Promise<T> {
162
- try {
163
- const response = await fetch(this.url);
164
-
165
- if (!response.ok) {
166
- throw new Error(`HTTP error! status: ${response.status}`);
25
+ private pageId: string | null = null;
26
+
27
+ constructor(url: string, options: IncludeOptions = {}) {
28
+ this.url = url;
29
+ this.options = options;
30
+
31
+ // Auto-detect type from extension if not provided
32
+ if (!options.type) {
33
+ if (url.endsWith('.css')) {
34
+ this.options.type = 'css';
35
+ } else if (url.endsWith('.mjs') || url.endsWith('.module.js')) {
36
+ this.options.type = 'module';
37
+ } else {
38
+ this.options.type = 'js';
167
39
  }
168
-
169
- const data = await response.json();
170
- console.log(`✓ JSON loaded: ${this.url}`);
171
- return data;
172
- } catch (error: any) {
173
- throw error;
174
40
  }
175
41
  }
176
42
 
177
- /**
178
- * Fetch JSON and execute callback with data
179
- *
180
- * Usage:
181
- * jux.include('config.json').onJson(data => {
182
- * console.log('Config:', data);
183
- * });
184
- */
185
- onJson<T = any>(callback: (data: T) => void): this {
186
- this.asJson<T>().then(callback).catch(error => {
187
- console.error('Failed to load JSON:', error);
188
- });
189
- return this;
190
- }
191
-
192
43
  /* -------------------------
193
- * Options
44
+ * Fluent API
194
45
  * ------------------------- */
195
46
 
196
- with(options: IncludeOptions): this {
197
- this.options = { ...this.options, ...options };
47
+ css(): this {
48
+ this.options.type = 'css';
198
49
  return this;
199
50
  }
200
51
 
201
- inHead(): this {
202
- this.options.location = 'head';
52
+ js(): this {
53
+ this.options.type = 'js';
203
54
  return this;
204
55
  }
205
56
 
206
- inBody(): this {
207
- this.options.location = 'body-end';
57
+ module(): this {
58
+ this.options.type = 'module';
208
59
  return this;
209
60
  }
210
61
 
@@ -218,13 +69,28 @@ export class Include {
218
69
  return this;
219
70
  }
220
71
 
221
- crossOrigin(value: 'anonymous' | 'use-credentials' = 'anonymous'): this {
222
- this.options.crossOrigin = value;
72
+ /**
73
+ * Inject into specific container instead of <head>
74
+ * Useful for page-specific styles
75
+ *
76
+ * @example
77
+ * jux.include('/css/page1.css').into('#page1-container');
78
+ */
79
+ into(selector: string): this {
80
+ this.options.target = selector;
223
81
  return this;
224
82
  }
225
83
 
226
- integrity(hash: string): this {
227
- this.options.integrity = hash;
84
+ /**
85
+ * Mark as page-scoped for automatic cleanup
86
+ *
87
+ * @example
88
+ * jux.include('/css/dashboard.css').forPage('dashboard');
89
+ * // Later: Include.cleanupPage('dashboard');
90
+ */
91
+ forPage(pageId: string): this {
92
+ this.pageId = pageId;
93
+ this.options.pageScoped = true;
228
94
  return this;
229
95
  }
230
96
 
@@ -235,50 +101,45 @@ export class Include {
235
101
  render(): this {
236
102
  if (typeof document === 'undefined') return this;
237
103
 
238
- // Don't render JSON type (it's fetched via asJson() instead)
239
- if (this.type === 'json') {
240
- console.warn('Include: JSON files should be loaded with .asJson() instead of .render()');
241
- return this;
242
- }
243
-
244
104
  try {
245
- this.remove();
105
+ // Check if already loaded
106
+ if (this.isAlreadyLoaded()) {
107
+ console.log(`⚠️ Resource already loaded: ${this.url}`);
108
+ return this;
109
+ }
246
110
 
111
+ // Create element based on type
247
112
  let element: HTMLElement;
248
113
 
249
- switch (this.type) {
250
- case 'stylesheet':
114
+ switch (this.options.type) {
115
+ case 'css':
251
116
  element = this.createStylesheet();
252
117
  break;
253
- case 'script':
118
+ case 'js':
254
119
  case 'module':
255
120
  element = this.createScript();
256
121
  break;
257
- case 'image':
258
- case 'font':
259
- case 'preload':
260
- case 'prefetch':
261
- element = this.createLink();
262
- break;
263
122
  default:
264
- throw new Error(`Unknown include type: ${this.type}`);
123
+ throw new Error(`Unknown resource type: ${this.options.type}`);
265
124
  }
266
125
 
126
+ // Get target container
267
127
  const container = this.getContainer();
268
- const location = this.options.location || 'head';
269
-
270
- if (location === 'body-end') {
271
- container.appendChild(element);
272
- } else {
273
- container.insertBefore(element, container.firstChild);
274
- }
128
+ container.appendChild(element);
275
129
 
276
130
  this.element = element;
277
131
 
278
- // Log with type indicator
279
- const typeIndicator = this.explicitType ? '(explicit)' : '(auto-detected)';
280
- console.log(`✓ Include loaded as ${this.type} ${typeIndicator}: ${this.url}`);
132
+ // Register for page cleanup if needed
133
+ if (this.options.pageScoped && this.pageId) {
134
+ if (!scopedResources.has(this.pageId)) {
135
+ scopedResources.set(this.pageId, new Set());
136
+ }
137
+ scopedResources.get(this.pageId)!.add(element);
138
+ }
139
+
140
+ console.log(`✓ Loaded ${this.options.type}: ${this.url}`);
281
141
  } catch (error: any) {
142
+ console.error(`✗ Failed to load ${this.options.type}: ${this.url}`, error);
282
143
  throw error;
283
144
  }
284
145
 
@@ -293,12 +154,15 @@ export class Include {
293
154
  const link = document.createElement('link');
294
155
  link.rel = 'stylesheet';
295
156
  link.href = this.url;
157
+ link.dataset.juxInclude = this.url;
296
158
 
297
159
  if (this.options.crossOrigin) link.crossOrigin = this.options.crossOrigin;
298
160
  if (this.options.integrity) link.integrity = this.options.integrity;
299
161
 
300
162
  link.onload = () => console.log(`✓ Stylesheet loaded: ${this.url}`);
301
- link.onerror = () => this.handleError('stylesheet');
163
+ link.onerror = () => {
164
+ throw new Error(`Failed to load stylesheet: ${this.url}`);
165
+ };
302
166
 
303
167
  return link;
304
168
  }
@@ -306,66 +170,42 @@ export class Include {
306
170
  private createScript(): HTMLScriptElement {
307
171
  const script = document.createElement('script');
308
172
  script.src = this.url;
173
+ script.dataset.juxInclude = this.url;
309
174
 
310
- if (this.type === 'module') script.type = 'module';
175
+ if (this.options.type === 'module') script.type = 'module';
311
176
  if (this.options.async) script.async = true;
312
177
  if (this.options.defer) script.defer = true;
313
178
  if (this.options.crossOrigin) script.crossOrigin = this.options.crossOrigin;
314
179
  if (this.options.integrity) script.integrity = this.options.integrity;
315
180
 
316
181
  script.onload = () => console.log(`✓ Script loaded: ${this.url}`);
317
- script.onerror = () => this.handleError('script');
182
+ script.onerror = () => {
183
+ throw new Error(`Failed to load script: ${this.url}`);
184
+ };
318
185
 
319
186
  return script;
320
187
  }
321
188
 
322
- private createLink(): HTMLLinkElement {
323
- const link = document.createElement('link');
324
-
325
- // Set rel based on type
326
- if (this.type === 'prefetch') {
327
- link.rel = 'prefetch';
328
- } else {
329
- link.rel = 'preload';
330
- }
331
-
332
- link.href = this.url;
333
-
334
- // Set 'as' attribute
335
- if (this.type === 'image') {
336
- link.as = 'image';
337
- } else if (this.type === 'font') {
338
- link.as = 'font';
339
- link.crossOrigin = this.options.crossOrigin || 'anonymous';
340
- } else if (this.options.as) {
341
- link.as = this.options.as;
342
- }
343
-
344
- if (this.options.crossOrigin && this.type !== 'font') {
345
- link.crossOrigin = this.options.crossOrigin;
346
- }
347
- if (this.options.integrity) {
348
- link.integrity = this.options.integrity;
349
- }
350
-
351
- return link;
352
- }
353
-
354
189
  /* -------------------------
355
190
  * Helpers
356
191
  * ------------------------- */
357
192
 
358
193
  private getContainer(): HTMLElement {
359
- const location = this.options.location || 'head';
360
- return location === 'head' ? document.head : document.body;
194
+ if (this.options.target) {
195
+ const container = document.querySelector(this.options.target);
196
+ if (!container) {
197
+ throw new Error(`Target container not found: ${this.options.target}`);
198
+ }
199
+ return container as HTMLElement;
200
+ }
201
+ return document.head;
361
202
  }
362
203
 
363
- private handleError(type: string): void {
364
- const error = new Error(`Failed to load ${type}: ${this.url}`);
365
- throw error;
204
+ private isAlreadyLoaded(): boolean {
205
+ const selector = `[data-jux-include="${this.url}"]`;
206
+ return document.querySelector(selector) !== null;
366
207
  }
367
208
 
368
-
369
209
  remove(): this {
370
210
  if (this.element?.parentNode) {
371
211
  this.element.parentNode.removeChild(this.element);
@@ -373,38 +213,69 @@ export class Include {
373
213
  }
374
214
  return this;
375
215
  }
216
+
217
+ /* -------------------------
218
+ * Static Page Cleanup
219
+ * ------------------------- */
220
+
221
+ /**
222
+ * Remove all resources for a specific page
223
+ *
224
+ * @example
225
+ * Include.cleanupPage('dashboard');
226
+ */
227
+ static cleanupPage(pageId: string): void {
228
+ const resources = scopedResources.get(pageId);
229
+ if (!resources) return;
230
+
231
+ resources.forEach(element => {
232
+ element.parentNode?.removeChild(element);
233
+ });
234
+
235
+ scopedResources.delete(pageId);
236
+ console.log(`✓ Cleaned up page resources: ${pageId}`);
237
+ }
238
+
239
+ /**
240
+ * Remove all page-scoped resources
241
+ */
242
+ static cleanupAll(): void {
243
+ scopedResources.forEach((resources, pageId) => {
244
+ resources.forEach(element => {
245
+ element.parentNode?.removeChild(element);
246
+ });
247
+ });
248
+ scopedResources.clear();
249
+ console.log('✓ Cleaned up all page-scoped resources');
250
+ }
376
251
  }
377
252
 
378
253
  /**
379
- * Factory function - auto-detects type and renders immediately
254
+ * Factory function - simplified usage
380
255
  *
381
256
  * Usage:
382
- * // Auto-detect (works for most cases)
383
- * jux.include('styles.css');
384
- * jux.include('script.js').async();
257
+ * // Basic (auto-detects from extension)
258
+ * jux.include('/css/styles.css');
259
+ * jux.include('/js/app.js');
385
260
  *
386
- * // Explicit type (for extensionless URLs)
387
- * jux.include('https://cdn.tailwindcss.com').withJs();
388
- * jux.include('https://cdn.tailwindcss.com').asScript();
389
- * jux.include('https://unpkg.com/htmx.org').withJs({ defer: true });
261
+ * // Page-specific with cleanup
262
+ * jux.include('/css/dashboard.css').forPage('dashboard');
263
+ * jux.include('/js/dashboard.js').forPage('dashboard');
390
264
  *
391
- * // CDN with parameters
392
- * jux.include('https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4').withJs();
265
+ * // Later cleanup:
266
+ * Include.cleanupPage('dashboard');
393
267
  *
394
- * // Module
395
- * jux.include('app.mjs').withModule();
268
+ * // Inject into specific container
269
+ * jux.include('/css/widget.css').into('#widget-container');
270
+ *
271
+ * // External CDN
272
+ * jux.include('https://cdn.tailwindcss.com').js();
396
273
  *
397
- * // For JSON:
398
- * const data = await jux.include('config.json').asJson();
399
- * jux.include('data.json').onJson(data => console.log(data));
274
+ * // Module
275
+ * jux.include('/js/app.mjs').module();
400
276
  */
401
- export function include(urlOrFile: string): Include {
402
- const imp = new Include(urlOrFile);
403
-
404
- // Don't auto-render JSON files
405
- if (imp['type'] !== 'json') {
406
- imp.render();
407
- }
408
-
409
- return imp;
277
+ export function include(url: string, options?: IncludeOptions): Include {
278
+ const inc = new Include(url, options);
279
+ inc.render();
280
+ return inc;
410
281
  }
@@ -9,7 +9,7 @@ export interface ModalOptions {
9
9
  icon?: EmojiIconType | undefined;
10
10
  close?: boolean;
11
11
  backdropClose?: boolean;
12
- position?: 'bottom' | 'center' | 'top' | 'fullscreen' | 'popover';
12
+ position?: 'bottom' | 'center' | 'top' | 'fullscreen' | 'popover' | 'popover-top' | 'popover-bottom' | 'popover-left' | 'popover-right';
13
13
  size?: 'small' | 'medium' | 'large';
14
14
  actions?: Array<{
15
15
  label: string;
@@ -25,7 +25,7 @@ type ModalState = {
25
25
  icon: EmojiIconType | undefined;
26
26
  close: boolean;
27
27
  backdropClose: boolean;
28
- position?: 'bottom' | 'center' | 'top' | 'fullscreen' | 'popover';
28
+ position?: 'bottom' | 'center' | 'top' | 'fullscreen' | 'popover' | 'popover-top' | 'popover-bottom' | 'popover-left' | 'popover-right';
29
29
  size: string;
30
30
  open: boolean;
31
31
  actions: Array<{
@@ -52,6 +52,7 @@ export declare class Modal extends BaseComponent<ModalState> {
52
52
  close(value: boolean): this;
53
53
  backdropClose(value: boolean): this;
54
54
  size(value: 'small' | 'medium' | 'large'): this;
55
+ position(value: 'bottom' | 'center' | 'top' | 'fullscreen' | 'popover' | 'popover-top' | 'popover-bottom' | 'popover-left' | 'popover-right'): this;
55
56
  actions(value: Array<{
56
57
  label: string;
57
58
  variant?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"modal.d.ts","sourceRoot":"","sources":["modal.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"modal.d.ts","sourceRoot":"","sources":["modal.ts"],"names":[],"mappings":"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"}
@@ -58,6 +58,17 @@ export class Modal extends BaseComponent {
58
58
  case 'size':
59
59
  modal.className = modal.className.replace(/jux-modal-(small|medium|large)/, `jux-modal-${value}`);
60
60
  break;
61
+ case 'position':
62
+ // Remove existing position classes
63
+ this._overlay.className = this._overlay.className
64
+ .split(' ')
65
+ .filter(c => !c.startsWith('jux-modal-position-'))
66
+ .join(' ');
67
+ // Add new position class
68
+ if (value) {
69
+ this._overlay.classList.add(`jux-modal-position-${value}`);
70
+ }
71
+ break;
61
72
  case 'actions':
62
73
  this._rebuildActions();
63
74
  break;
@@ -189,6 +200,10 @@ export class Modal extends BaseComponent {
189
200
  this.state.size = value;
190
201
  return this;
191
202
  }
203
+ position(value) {
204
+ this.state.position = value;
205
+ return this;
206
+ }
192
207
  actions(value) {
193
208
  this.state.actions = value;
194
209
  return this;
@@ -275,12 +290,16 @@ export class Modal extends BaseComponent {
275
290
  * ═════════════════════════════════════════════════════════════════ */
276
291
  render(targetId) {
277
292
  const container = this._setupContainer(targetId);
278
- const { open, title, content, icon, size, close, backdropClose, actions, style, class: className } = this.state;
293
+ const { open, title, content, icon, size, position, close, backdropClose, actions, style, class: className } = this.state;
279
294
  const hasOpenSync = this._syncBindings.some(b => b.property === 'open');
280
295
  const overlay = document.createElement('div');
281
296
  overlay.className = 'jux-modal-overlay';
282
297
  overlay.id = this._id;
283
298
  overlay.style.display = open ? 'flex' : 'none';
299
+ // Add position class
300
+ if (position) {
301
+ overlay.classList.add(`jux-modal-position-${position}`);
302
+ }
284
303
  if (className)
285
304
  overlay.className += ` ${className}`;
286
305
  if (style)