juxscript 1.0.2 → 1.0.4
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/README.md +37 -92
- package/bin/cli.js +57 -56
- package/lib/components/alert.ts +240 -0
- package/lib/components/app.ts +216 -82
- package/lib/components/badge.ts +164 -0
- package/lib/components/button.ts +188 -53
- package/lib/components/card.ts +75 -61
- package/lib/components/chart.ts +17 -15
- package/lib/components/checkbox.ts +228 -0
- package/lib/components/code.ts +66 -152
- package/lib/components/container.ts +104 -208
- package/lib/components/data.ts +1 -3
- package/lib/components/datepicker.ts +226 -0
- package/lib/components/dialog.ts +258 -0
- package/lib/components/docs-data.json +1697 -388
- package/lib/components/dropdown.ts +244 -0
- package/lib/components/element.ts +271 -0
- package/lib/components/fileupload.ts +319 -0
- package/lib/components/footer.ts +37 -18
- package/lib/components/header.ts +53 -33
- package/lib/components/heading.ts +119 -0
- package/lib/components/helpers.ts +34 -0
- package/lib/components/hero.ts +57 -31
- package/lib/components/include.ts +292 -0
- package/lib/components/input.ts +166 -78
- package/lib/components/layout.ts +144 -18
- package/lib/components/list.ts +83 -74
- package/lib/components/loading.ts +263 -0
- package/lib/components/main.ts +43 -17
- package/lib/components/menu.ts +108 -24
- package/lib/components/modal.ts +50 -21
- package/lib/components/nav.ts +60 -18
- package/lib/components/paragraph.ts +111 -0
- package/lib/components/progress.ts +276 -0
- package/lib/components/radio.ts +236 -0
- package/lib/components/req.ts +300 -0
- package/lib/components/script.ts +33 -74
- package/lib/components/select.ts +247 -0
- package/lib/components/sidebar.ts +86 -36
- package/lib/components/style.ts +47 -70
- package/lib/components/switch.ts +261 -0
- package/lib/components/table.ts +47 -24
- package/lib/components/tabs.ts +105 -63
- package/lib/components/theme-toggle.ts +361 -0
- package/lib/components/token-calculator.ts +380 -0
- package/lib/components/tooltip.ts +244 -0
- package/lib/components/view.ts +36 -20
- package/lib/components/write.ts +284 -0
- package/lib/globals.d.ts +21 -0
- package/lib/jux.ts +172 -68
- package/lib/presets/notion.css +521 -0
- package/lib/presets/notion.jux +27 -0
- package/lib/reactivity/state.ts +364 -0
- package/machinery/compiler.js +126 -38
- package/machinery/generators/html.js +2 -3
- package/machinery/server.js +2 -2
- package/package.json +29 -3
- package/lib/components/import.ts +0 -430
- package/lib/components/node.ts +0 -200
- package/lib/components/reactivity.js +0 -104
- package/lib/components/theme.ts +0 -97
- package/lib/layouts/notion.css +0 -258
- package/lib/styles/base-theme.css +0 -186
- package/lib/styles/dark-theme.css +0 -144
- package/lib/styles/light-theme.css +0 -144
- package/lib/styles/tokens/dark.css +0 -86
- package/lib/styles/tokens/light.css +0 -86
- package/lib/templates/index.juxt +0 -33
- package/lib/themes/dark.css +0 -86
- package/lib/themes/light.css +0 -86
- /package/lib/{styles → presets}/global.css +0 -0
package/package.json
CHANGED
|
@@ -1,23 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "juxscript",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A JavaScript UX authorship platform",
|
|
6
6
|
"main": "lib/jux.js",
|
|
7
|
-
"types": "
|
|
7
|
+
"types": "lib/jux.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/jux.d.ts",
|
|
11
|
+
"import": "./lib/jux.js",
|
|
12
|
+
"default": "./lib/jux.js"
|
|
13
|
+
},
|
|
14
|
+
"./lib/*": "./lib/*",
|
|
15
|
+
"./lib/components/*": "./lib/components/*",
|
|
16
|
+
"./package.json": "./package.json"
|
|
17
|
+
},
|
|
18
|
+
"typesVersions": {
|
|
19
|
+
"*": {
|
|
20
|
+
"*": [
|
|
21
|
+
"lib/*"
|
|
22
|
+
],
|
|
23
|
+
"lib/*": [
|
|
24
|
+
"lib/*"
|
|
25
|
+
],
|
|
26
|
+
"lib/components/*": [
|
|
27
|
+
"lib/components/*"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
8
31
|
"bin": {
|
|
9
32
|
"jux": "./bin/cli.js"
|
|
10
33
|
},
|
|
11
34
|
"scripts": {
|
|
12
35
|
"dev": "cd examples && npx jux serve",
|
|
13
36
|
"build:examples": "cd examples && rm -rf dist && npx jux build",
|
|
14
|
-
"
|
|
37
|
+
"build": "tsc",
|
|
38
|
+
"test": "node test/run-tests.js",
|
|
39
|
+
"generate:icons": "node scripts/generate-icon-types.js"
|
|
15
40
|
},
|
|
16
41
|
"files": [
|
|
17
42
|
"lib",
|
|
18
43
|
"bin",
|
|
19
44
|
"machinery",
|
|
20
45
|
"types",
|
|
46
|
+
"lib/**/*.d.ts",
|
|
21
47
|
"README.md",
|
|
22
48
|
"LICENSE"
|
|
23
49
|
],
|
package/lib/components/import.ts
DELETED
|
@@ -1,430 +0,0 @@
|
|
|
1
|
-
import { ErrorHandler } from './error-handler.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Import - Import external resources (CSS, JS, images, fonts, etc.)
|
|
5
|
-
* Provides flexible resource loading with control over injection location
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export type ImportType = 'stylesheet' | 'script' | 'image' | 'font' | 'preload' | 'prefetch' | 'module';
|
|
9
|
-
export type ImportLocation = 'head' | 'body-start' | 'body-end';
|
|
10
|
-
|
|
11
|
-
export interface ImportOptions {
|
|
12
|
-
type?: ImportType;
|
|
13
|
-
href?: string;
|
|
14
|
-
src?: string;
|
|
15
|
-
as?: string;
|
|
16
|
-
crossOrigin?: 'anonymous' | 'use-credentials';
|
|
17
|
-
integrity?: string;
|
|
18
|
-
async?: boolean;
|
|
19
|
-
defer?: boolean;
|
|
20
|
-
module?: boolean;
|
|
21
|
-
location?: ImportLocation;
|
|
22
|
-
onLoad?: () => void;
|
|
23
|
-
onError?: (error: Error) => void;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export class Import {
|
|
27
|
-
private _type: ImportType = 'stylesheet';
|
|
28
|
-
private _href: string = '';
|
|
29
|
-
private _src: string = '';
|
|
30
|
-
private _as: string = '';
|
|
31
|
-
private _crossOrigin?: 'anonymous' | 'use-credentials';
|
|
32
|
-
private _integrity?: string;
|
|
33
|
-
private _async: boolean = false;
|
|
34
|
-
private _defer: boolean = false;
|
|
35
|
-
private _module: boolean = false;
|
|
36
|
-
private _location: ImportLocation = 'head';
|
|
37
|
-
private _onLoad?: () => void;
|
|
38
|
-
private _onError?: (error: Error) => void;
|
|
39
|
-
private _element: HTMLElement | null = null;
|
|
40
|
-
|
|
41
|
-
constructor(url: string, options: ImportOptions = {}) {
|
|
42
|
-
// Auto-detect type from URL if not specified
|
|
43
|
-
if (!options.type) {
|
|
44
|
-
if (url.endsWith('.css')) {
|
|
45
|
-
this._type = 'stylesheet';
|
|
46
|
-
} else if (url.endsWith('.js') || url.endsWith('.mjs')) {
|
|
47
|
-
this._type = 'script';
|
|
48
|
-
} else if (url.match(/\.(png|jpg|jpeg|gif|svg|webp)$/i)) {
|
|
49
|
-
this._type = 'image';
|
|
50
|
-
} else if (url.match(/\.(woff|woff2|ttf|otf|eot)$/i)) {
|
|
51
|
-
this._type = 'font';
|
|
52
|
-
} else {
|
|
53
|
-
this._type = 'preload';
|
|
54
|
-
}
|
|
55
|
-
} else {
|
|
56
|
-
this._type = options.type;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Set URL based on type
|
|
60
|
-
if (this._type === 'script') {
|
|
61
|
-
this._src = url;
|
|
62
|
-
} else {
|
|
63
|
-
this._href = url;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Apply options
|
|
67
|
-
if (options.as) this._as = options.as;
|
|
68
|
-
if (options.crossOrigin) this._crossOrigin = options.crossOrigin;
|
|
69
|
-
if (options.integrity) this._integrity = options.integrity;
|
|
70
|
-
if (options.async !== undefined) this._async = options.async;
|
|
71
|
-
if (options.defer !== undefined) this._defer = options.defer;
|
|
72
|
-
if (options.module !== undefined) this._module = options.module;
|
|
73
|
-
if (options.location) this._location = options.location;
|
|
74
|
-
if (options.onLoad) this._onLoad = options.onLoad;
|
|
75
|
-
if (options.onError) this._onError = options.onError;
|
|
76
|
-
|
|
77
|
-
// Auto-render
|
|
78
|
-
this.render();
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Set the resource type
|
|
83
|
-
*/
|
|
84
|
-
type(type: ImportType): this {
|
|
85
|
-
this._type = type;
|
|
86
|
-
return this;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Set the URL (for link elements)
|
|
91
|
-
*/
|
|
92
|
-
href(url: string): this {
|
|
93
|
-
this._href = url;
|
|
94
|
-
return this;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Set the URL (for script elements)
|
|
99
|
-
*/
|
|
100
|
-
src(url: string): this {
|
|
101
|
-
this._src = url;
|
|
102
|
-
return this;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Set the 'as' attribute (for preload/prefetch)
|
|
107
|
-
*/
|
|
108
|
-
as(value: string): this {
|
|
109
|
-
this._as = value;
|
|
110
|
-
return this;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Set crossorigin attribute
|
|
115
|
-
*/
|
|
116
|
-
crossOrigin(value: 'anonymous' | 'use-credentials'): this {
|
|
117
|
-
this._crossOrigin = value;
|
|
118
|
-
return this;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Set integrity hash for SRI (Subresource Integrity)
|
|
123
|
-
*/
|
|
124
|
-
integrity(hash: string): this {
|
|
125
|
-
this._integrity = hash;
|
|
126
|
-
return this;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Enable async loading (for scripts)
|
|
131
|
-
*/
|
|
132
|
-
async(value: boolean = true): this {
|
|
133
|
-
this._async = value;
|
|
134
|
-
return this;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Enable defer loading (for scripts)
|
|
139
|
-
*/
|
|
140
|
-
defer(value: boolean = true): this {
|
|
141
|
-
this._defer = value;
|
|
142
|
-
return this;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Mark as ES module (for scripts)
|
|
147
|
-
*/
|
|
148
|
-
module(value: boolean = true): this {
|
|
149
|
-
this._module = value;
|
|
150
|
-
return this;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Set injection location
|
|
155
|
-
*/
|
|
156
|
-
location(loc: ImportLocation): this {
|
|
157
|
-
this._location = loc;
|
|
158
|
-
return this;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Set onload callback
|
|
163
|
-
*/
|
|
164
|
-
onLoad(callback: () => void): this {
|
|
165
|
-
this._onLoad = callback;
|
|
166
|
-
return this;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Set onerror callback
|
|
171
|
-
*/
|
|
172
|
-
onError(callback: (error: Error) => void): this {
|
|
173
|
-
this._onError = callback;
|
|
174
|
-
return this;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Get the target container for injection
|
|
179
|
-
*/
|
|
180
|
-
private getContainer(): HTMLElement {
|
|
181
|
-
switch (this._location) {
|
|
182
|
-
case 'head':
|
|
183
|
-
return document.head;
|
|
184
|
-
case 'body-start':
|
|
185
|
-
return document.body;
|
|
186
|
-
case 'body-end':
|
|
187
|
-
return document.body;
|
|
188
|
-
default:
|
|
189
|
-
return document.head;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Render the import element
|
|
195
|
-
*/
|
|
196
|
-
render(): this {
|
|
197
|
-
if (typeof document === 'undefined') {
|
|
198
|
-
return this;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
try {
|
|
202
|
-
// Remove existing element
|
|
203
|
-
this.remove();
|
|
204
|
-
|
|
205
|
-
let element: HTMLElement;
|
|
206
|
-
|
|
207
|
-
switch (this._type) {
|
|
208
|
-
case 'stylesheet':
|
|
209
|
-
element = this.createStylesheet();
|
|
210
|
-
break;
|
|
211
|
-
case 'script':
|
|
212
|
-
element = this.createScript();
|
|
213
|
-
break;
|
|
214
|
-
case 'image':
|
|
215
|
-
element = this.createImage();
|
|
216
|
-
break;
|
|
217
|
-
case 'font':
|
|
218
|
-
element = this.createFont();
|
|
219
|
-
break;
|
|
220
|
-
case 'preload':
|
|
221
|
-
element = this.createPreload();
|
|
222
|
-
break;
|
|
223
|
-
case 'prefetch':
|
|
224
|
-
element = this.createPrefetch();
|
|
225
|
-
break;
|
|
226
|
-
case 'module':
|
|
227
|
-
element = this.createModule();
|
|
228
|
-
break;
|
|
229
|
-
default:
|
|
230
|
-
throw new Error(`Unknown import type: ${this._type}`);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const container = this.getContainer();
|
|
234
|
-
|
|
235
|
-
if (this._location === 'body-end') {
|
|
236
|
-
container.appendChild(element);
|
|
237
|
-
} else {
|
|
238
|
-
container.insertBefore(element, container.firstChild);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
this._element = element;
|
|
242
|
-
} catch (error: any) {
|
|
243
|
-
ErrorHandler.captureError({
|
|
244
|
-
component: 'Import',
|
|
245
|
-
method: 'render',
|
|
246
|
-
message: error.message,
|
|
247
|
-
stack: error.stack,
|
|
248
|
-
timestamp: new Date(),
|
|
249
|
-
context: {
|
|
250
|
-
type: this._type,
|
|
251
|
-
href: this._href,
|
|
252
|
-
src: this._src,
|
|
253
|
-
location: this._location,
|
|
254
|
-
error: 'runtime_exception'
|
|
255
|
-
}
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
return this;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Create a stylesheet link element
|
|
264
|
-
*/
|
|
265
|
-
private createStylesheet(): HTMLLinkElement {
|
|
266
|
-
const link = document.createElement('link');
|
|
267
|
-
link.rel = 'stylesheet';
|
|
268
|
-
link.href = this._href;
|
|
269
|
-
|
|
270
|
-
if (this._crossOrigin) link.crossOrigin = this._crossOrigin;
|
|
271
|
-
if (this._integrity) link.integrity = this._integrity;
|
|
272
|
-
|
|
273
|
-
link.onload = () => {
|
|
274
|
-
console.log(`✓ Stylesheet loaded: ${this._href}`);
|
|
275
|
-
if (this._onLoad) this._onLoad();
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
link.onerror = () => {
|
|
279
|
-
const error = new Error(`Failed to load stylesheet: ${this._href}`);
|
|
280
|
-
ErrorHandler.captureError({
|
|
281
|
-
component: 'Import',
|
|
282
|
-
method: 'createStylesheet',
|
|
283
|
-
message: error.message,
|
|
284
|
-
timestamp: new Date(),
|
|
285
|
-
context: { href: this._href, type: 'stylesheet', error: 'load_failed' }
|
|
286
|
-
});
|
|
287
|
-
if (this._onError) this._onError(error);
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
return link;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Create a script element
|
|
295
|
-
*/
|
|
296
|
-
private createScript(): HTMLScriptElement {
|
|
297
|
-
const script = document.createElement('script');
|
|
298
|
-
script.src = this._src;
|
|
299
|
-
|
|
300
|
-
if (this._async) script.async = true;
|
|
301
|
-
if (this._defer) script.defer = true;
|
|
302
|
-
if (this._module) script.type = 'module';
|
|
303
|
-
if (this._crossOrigin) script.crossOrigin = this._crossOrigin;
|
|
304
|
-
if (this._integrity) script.integrity = this._integrity;
|
|
305
|
-
|
|
306
|
-
script.onload = () => {
|
|
307
|
-
console.log(`✓ Script loaded: ${this._src}`);
|
|
308
|
-
if (this._onLoad) this._onLoad();
|
|
309
|
-
};
|
|
310
|
-
|
|
311
|
-
script.onerror = () => {
|
|
312
|
-
const error = new Error(`Failed to load script: ${this._src}`);
|
|
313
|
-
ErrorHandler.captureError({
|
|
314
|
-
component: 'Import',
|
|
315
|
-
method: 'createScript',
|
|
316
|
-
message: error.message,
|
|
317
|
-
timestamp: new Date(),
|
|
318
|
-
context: { src: this._src, type: 'script', error: 'load_failed' }
|
|
319
|
-
});
|
|
320
|
-
if (this._onError) this._onError(error);
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
return script;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* Create an image preload link
|
|
328
|
-
*/
|
|
329
|
-
private createImage(): HTMLLinkElement {
|
|
330
|
-
const link = document.createElement('link');
|
|
331
|
-
link.rel = 'preload';
|
|
332
|
-
link.as = 'image';
|
|
333
|
-
link.href = this._href;
|
|
334
|
-
|
|
335
|
-
if (this._crossOrigin) link.crossOrigin = this._crossOrigin;
|
|
336
|
-
|
|
337
|
-
return link;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Create a font preload link
|
|
342
|
-
*/
|
|
343
|
-
private createFont(): HTMLLinkElement {
|
|
344
|
-
const link = document.createElement('link');
|
|
345
|
-
link.rel = 'preload';
|
|
346
|
-
link.as = 'font';
|
|
347
|
-
link.href = this._href;
|
|
348
|
-
link.crossOrigin = this._crossOrigin || 'anonymous';
|
|
349
|
-
|
|
350
|
-
return link;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* Create a preload link
|
|
355
|
-
*/
|
|
356
|
-
private createPreload(): HTMLLinkElement {
|
|
357
|
-
const link = document.createElement('link');
|
|
358
|
-
link.rel = 'preload';
|
|
359
|
-
link.href = this._href;
|
|
360
|
-
|
|
361
|
-
if (this._as) link.as = this._as;
|
|
362
|
-
if (this._crossOrigin) link.crossOrigin = this._crossOrigin;
|
|
363
|
-
if (this._integrity) link.integrity = this._integrity;
|
|
364
|
-
|
|
365
|
-
return link;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Create a prefetch link
|
|
370
|
-
*/
|
|
371
|
-
private createPrefetch(): HTMLLinkElement {
|
|
372
|
-
const link = document.createElement('link');
|
|
373
|
-
link.rel = 'prefetch';
|
|
374
|
-
link.href = this._href;
|
|
375
|
-
|
|
376
|
-
if (this._as) link.as = this._as;
|
|
377
|
-
if (this._crossOrigin) link.crossOrigin = this._crossOrigin;
|
|
378
|
-
|
|
379
|
-
return link;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Create a module script
|
|
384
|
-
*/
|
|
385
|
-
private createModule(): HTMLScriptElement {
|
|
386
|
-
const script = document.createElement('script');
|
|
387
|
-
script.type = 'module';
|
|
388
|
-
script.src = this._src;
|
|
389
|
-
|
|
390
|
-
if (this._crossOrigin) script.crossOrigin = this._crossOrigin;
|
|
391
|
-
if (this._integrity) script.integrity = this._integrity;
|
|
392
|
-
|
|
393
|
-
script.onload = () => {
|
|
394
|
-
console.log(`✓ Module loaded: ${this._src}`);
|
|
395
|
-
if (this._onLoad) this._onLoad();
|
|
396
|
-
};
|
|
397
|
-
|
|
398
|
-
script.onerror = () => {
|
|
399
|
-
const error = new Error(`Failed to load module: ${this._src}`);
|
|
400
|
-
ErrorHandler.captureError({
|
|
401
|
-
component: 'Import',
|
|
402
|
-
method: 'createModule',
|
|
403
|
-
message: error.message,
|
|
404
|
-
timestamp: new Date(),
|
|
405
|
-
context: { src: this._src, type: 'module', error: 'load_failed' }
|
|
406
|
-
});
|
|
407
|
-
if (this._onError) this._onError(error);
|
|
408
|
-
};
|
|
409
|
-
|
|
410
|
-
return script;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
/**
|
|
414
|
-
* Remove the element from the document
|
|
415
|
-
*/
|
|
416
|
-
remove(): this {
|
|
417
|
-
if (this._element && this._element.parentNode) {
|
|
418
|
-
this._element.parentNode.removeChild(this._element);
|
|
419
|
-
this._element = null;
|
|
420
|
-
}
|
|
421
|
-
return this;
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
/**
|
|
426
|
-
* Factory function for creating imports
|
|
427
|
-
*/
|
|
428
|
-
export function importResource(url: string, options?: ImportOptions): Import {
|
|
429
|
-
return new Import(url, options);
|
|
430
|
-
}
|
package/lib/components/node.ts
DELETED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import { Reactive, getOrCreateContainer } from './reactivity.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Node component options
|
|
5
|
-
*/
|
|
6
|
-
export interface NodeOptions {
|
|
7
|
-
tagType?: string;
|
|
8
|
-
className?: string;
|
|
9
|
-
textContent?: string;
|
|
10
|
-
innerHTML?: string;
|
|
11
|
-
attributes?: Record<string, string>;
|
|
12
|
-
styles?: Record<string, string>;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Node component state
|
|
17
|
-
*/
|
|
18
|
-
type NodeState = {
|
|
19
|
-
tagType: string;
|
|
20
|
-
className: string;
|
|
21
|
-
textContent: string;
|
|
22
|
-
innerHTML: string;
|
|
23
|
-
attributes: Record<string, string>;
|
|
24
|
-
styles: Record<string, string>;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Node component - Create arbitrary HTML elements
|
|
29
|
-
*
|
|
30
|
-
* Usage:
|
|
31
|
-
* const div = jux.node('myDiv', { tagType: 'div', className: 'container' });
|
|
32
|
-
* div.render('#target');
|
|
33
|
-
*
|
|
34
|
-
* const span = jux.node('mySpan')
|
|
35
|
-
* .tagType('span')
|
|
36
|
-
* .className('highlight')
|
|
37
|
-
* .textContent('Hello World')
|
|
38
|
-
* .render();
|
|
39
|
-
*/
|
|
40
|
-
export class Node extends Reactive {
|
|
41
|
-
state!: NodeState;
|
|
42
|
-
container: HTMLElement | null = null;
|
|
43
|
-
|
|
44
|
-
constructor(componentId: string, options: NodeOptions = {}) {
|
|
45
|
-
super();
|
|
46
|
-
this._setComponentId(componentId);
|
|
47
|
-
|
|
48
|
-
this.state = this._createReactiveState({
|
|
49
|
-
tagType: options.tagType ?? 'div',
|
|
50
|
-
className: options.className ?? '',
|
|
51
|
-
textContent: options.textContent ?? '',
|
|
52
|
-
innerHTML: options.innerHTML ?? '',
|
|
53
|
-
attributes: options.attributes ?? {},
|
|
54
|
-
styles: options.styles ?? {}
|
|
55
|
-
}) as NodeState;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/* -------------------------
|
|
59
|
-
* Fluent API
|
|
60
|
-
* ------------------------- */
|
|
61
|
-
|
|
62
|
-
tagType(value: string): this {
|
|
63
|
-
this.state.tagType = value;
|
|
64
|
-
return this;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
className(value: string): this {
|
|
68
|
-
this.state.className = value;
|
|
69
|
-
return this;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
addClass(value: string): this {
|
|
73
|
-
const classes = this.state.className.split(' ').filter(c => c);
|
|
74
|
-
if (!classes.includes(value)) {
|
|
75
|
-
classes.push(value);
|
|
76
|
-
this.state.className = classes.join(' ');
|
|
77
|
-
}
|
|
78
|
-
return this;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
removeClass(value: string): this {
|
|
82
|
-
const classes = this.state.className.split(' ').filter(c => c && c !== value);
|
|
83
|
-
this.state.className = classes.join(' ');
|
|
84
|
-
return this;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
textContent(value: string): this {
|
|
88
|
-
this.state.textContent = value;
|
|
89
|
-
this.state.innerHTML = ''; // Clear innerHTML when setting textContent
|
|
90
|
-
return this;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
innerHTML(value: string): this {
|
|
94
|
-
this.state.innerHTML = value;
|
|
95
|
-
this.state.textContent = ''; // Clear textContent when setting innerHTML
|
|
96
|
-
return this;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
attr(name: string, value: string): this {
|
|
100
|
-
this.state.attributes[name] = value;
|
|
101
|
-
return this;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
attrs(attributes: Record<string, string>): this {
|
|
105
|
-
this.state.attributes = { ...this.state.attributes, ...attributes };
|
|
106
|
-
return this;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
style(property: string, value: string): this {
|
|
110
|
-
this.state.styles[property] = value;
|
|
111
|
-
return this;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
styles(styles: Record<string, string>): this {
|
|
115
|
-
this.state.styles = { ...this.state.styles, ...styles };
|
|
116
|
-
return this;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/* -------------------------
|
|
120
|
-
* Render
|
|
121
|
-
* ------------------------- */
|
|
122
|
-
|
|
123
|
-
render(targetId?: string): this {
|
|
124
|
-
let container: HTMLElement;
|
|
125
|
-
|
|
126
|
-
if (targetId) {
|
|
127
|
-
// Use provided targetId - must exist
|
|
128
|
-
const target = document.querySelector(targetId);
|
|
129
|
-
if (!target || !(target instanceof HTMLElement)) {
|
|
130
|
-
throw new Error(`Node: Target element "${targetId}" not found`);
|
|
131
|
-
}
|
|
132
|
-
container = target;
|
|
133
|
-
} else {
|
|
134
|
-
// Create or get container with component ID
|
|
135
|
-
container = getOrCreateContainer(this._componentId) as HTMLElement;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
this.container = container;
|
|
139
|
-
const { tagType, className, textContent, innerHTML, attributes, styles } = this.state;
|
|
140
|
-
|
|
141
|
-
// Create the element
|
|
142
|
-
const element = document.createElement(tagType);
|
|
143
|
-
|
|
144
|
-
// Set ID to component ID
|
|
145
|
-
element.id = this._componentId;
|
|
146
|
-
|
|
147
|
-
// Set className
|
|
148
|
-
if (className) {
|
|
149
|
-
element.className = className;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Set content (innerHTML takes precedence over textContent)
|
|
153
|
-
if (innerHTML) {
|
|
154
|
-
element.innerHTML = innerHTML;
|
|
155
|
-
} else if (textContent) {
|
|
156
|
-
element.textContent = textContent;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Set attributes
|
|
160
|
-
Object.entries(attributes).forEach(([name, value]) => {
|
|
161
|
-
element.setAttribute(name, value);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
// Set styles
|
|
165
|
-
Object.entries(styles).forEach(([property, value]) => {
|
|
166
|
-
element.style.setProperty(property, value);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
container.appendChild(element);
|
|
170
|
-
return this;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Render to another Jux component's container
|
|
175
|
-
*
|
|
176
|
-
* Usage:
|
|
177
|
-
* const container = jux.node('myContainer');
|
|
178
|
-
* const child = jux.node('myChild').renderTo(container);
|
|
179
|
-
*/
|
|
180
|
-
renderTo(juxComponent: any): this {
|
|
181
|
-
// Verify it's a Jux component (has _componentId from Reactive base)
|
|
182
|
-
if (!juxComponent || typeof juxComponent !== 'object') {
|
|
183
|
-
throw new Error('Node.renderTo: Invalid component - not an object');
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (!juxComponent._componentId || typeof juxComponent._componentId !== 'string') {
|
|
187
|
-
throw new Error('Node.renderTo: Invalid component - missing _componentId (not a Jux component)');
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Render to the component's ID as a selector
|
|
191
|
-
return this.render(`#${juxComponent._componentId}`);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Factory helper
|
|
197
|
-
*/
|
|
198
|
-
export function node(componentId: string, options: NodeOptions = {}): Node {
|
|
199
|
-
return new Node(componentId, options);
|
|
200
|
-
}
|