juxscript 1.0.3 → 1.0.5

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 (73) hide show
  1. package/README.md +37 -92
  2. package/bin/cli.js +57 -56
  3. package/lib/components/alert.ts +240 -0
  4. package/lib/components/app.ts +216 -82
  5. package/lib/components/badge.ts +164 -0
  6. package/lib/components/barchart.ts +1248 -0
  7. package/lib/components/button.ts +188 -53
  8. package/lib/components/card.ts +75 -61
  9. package/lib/components/chart.ts +17 -15
  10. package/lib/components/checkbox.ts +199 -0
  11. package/lib/components/code.ts +66 -152
  12. package/lib/components/container.ts +104 -208
  13. package/lib/components/data.ts +1 -3
  14. package/lib/components/datepicker.ts +226 -0
  15. package/lib/components/dialog.ts +258 -0
  16. package/lib/components/docs-data.json +1969 -423
  17. package/lib/components/dropdown.ts +244 -0
  18. package/lib/components/element.ts +271 -0
  19. package/lib/components/fileupload.ts +319 -0
  20. package/lib/components/footer.ts +37 -18
  21. package/lib/components/header.ts +53 -33
  22. package/lib/components/heading.ts +119 -0
  23. package/lib/components/helpers.ts +34 -0
  24. package/lib/components/hero.ts +57 -31
  25. package/lib/components/include.ts +292 -0
  26. package/lib/components/input.ts +508 -77
  27. package/lib/components/layout.ts +144 -18
  28. package/lib/components/list.ts +83 -74
  29. package/lib/components/loading.ts +263 -0
  30. package/lib/components/main.ts +43 -17
  31. package/lib/components/menu.ts +108 -24
  32. package/lib/components/modal.ts +50 -21
  33. package/lib/components/nav.ts +60 -18
  34. package/lib/components/paragraph.ts +111 -0
  35. package/lib/components/progress.ts +276 -0
  36. package/lib/components/radio.ts +236 -0
  37. package/lib/components/req.ts +300 -0
  38. package/lib/components/script.ts +33 -74
  39. package/lib/components/select.ts +280 -0
  40. package/lib/components/sidebar.ts +87 -37
  41. package/lib/components/style.ts +47 -70
  42. package/lib/components/switch.ts +261 -0
  43. package/lib/components/table.ts +47 -24
  44. package/lib/components/tabs.ts +105 -63
  45. package/lib/components/theme-toggle.ts +361 -0
  46. package/lib/components/token-calculator.ts +380 -0
  47. package/lib/components/tooltip.ts +244 -0
  48. package/lib/components/view.ts +36 -20
  49. package/lib/components/write.ts +284 -0
  50. package/lib/globals.d.ts +21 -0
  51. package/lib/jux.ts +178 -68
  52. package/lib/presets/notion.css +521 -0
  53. package/lib/presets/notion.jux +27 -0
  54. package/lib/reactivity/state.ts +364 -0
  55. package/lib/themes/charts.js +126 -0
  56. package/machinery/compiler.js +126 -38
  57. package/machinery/generators/html.js +2 -3
  58. package/machinery/server.js +2 -2
  59. package/package.json +29 -3
  60. package/lib/components/import.ts +0 -430
  61. package/lib/components/node.ts +0 -200
  62. package/lib/components/reactivity.js +0 -104
  63. package/lib/components/theme.ts +0 -97
  64. package/lib/layouts/notion.css +0 -258
  65. package/lib/styles/base-theme.css +0 -186
  66. package/lib/styles/dark-theme.css +0 -144
  67. package/lib/styles/light-theme.css +0 -144
  68. package/lib/styles/tokens/dark.css +0 -86
  69. package/lib/styles/tokens/light.css +0 -86
  70. package/lib/templates/index.juxt +0 -33
  71. package/lib/themes/dark.css +0 -86
  72. package/lib/themes/light.css +0 -86
  73. /package/lib/{styles → presets}/global.css +0 -0
@@ -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
- }
@@ -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
- }