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,4 +1,4 @@
1
- import { Reactive, getOrCreateContainer } from './reactivity.js';
1
+ import { getOrCreateContainer } from './helpers.js';
2
2
 
3
3
  /**
4
4
  * View component options
@@ -29,19 +29,21 @@ type ViewState = {
29
29
  * view.add(component1);
30
30
  * await view.render();
31
31
  */
32
- export class View extends Reactive {
33
- state!: ViewState;
32
+ export class View {
33
+ state: ViewState;
34
34
  container: HTMLElement | null = null;
35
+ _id: string;
36
+ id: string;
35
37
 
36
- constructor(componentId: string, options: ViewOptions = {}) {
37
- super();
38
- this._setComponentId(componentId);
38
+ constructor(id: string, options: ViewOptions = {}) {
39
+ this._id = id;
40
+ this.id = id;
39
41
 
40
- this.state = this._createReactiveState({
42
+ this.state = {
41
43
  title: options.title ?? '',
42
44
  description: options.description ?? '',
43
- children: Array.isArray(options.children) ? [...options.children] : [],
44
- }) as ViewState;
45
+ children: Array.isArray(options.children) ? [...options.children] : []
46
+ };
45
47
  }
46
48
 
47
49
  /* -------------------------
@@ -81,7 +83,7 @@ export class View extends Reactive {
81
83
  }
82
84
  container = target;
83
85
  } else {
84
- container = getOrCreateContainer(this._componentId) as HTMLElement;
86
+ container = getOrCreateContainer(this._id);
85
87
  }
86
88
 
87
89
  this.container = container;
@@ -89,7 +91,7 @@ export class View extends Reactive {
89
91
 
90
92
  const view = document.createElement('div');
91
93
  view.className = 'jux-view';
92
- view.id = this._componentId;
94
+ view.id = this._id;
93
95
 
94
96
  // View header
95
97
  if (title || description) {
@@ -122,16 +124,26 @@ export class View extends Reactive {
122
124
  const child = children[i];
123
125
  if (!child) continue;
124
126
 
125
- let childId = (child as any)._componentId ?? `view-child-${i}`;
127
+ // Get child ID
128
+ let childId: string | null = null;
129
+ if ((child as any).id) {
130
+ childId = (child as any).id;
131
+ } else if ((child as any)._id) {
132
+ childId = (child as any)._id;
133
+ } else {
134
+ childId = `${this._id}-child-${i}`;
135
+ }
126
136
 
137
+ // Create wrapper for child
127
138
  const childWrapper = document.createElement('div');
128
139
  childWrapper.className = 'jux-view-item';
129
- childWrapper.id = childId;
140
+ childWrapper.id = `${childId}-wrapper`;
130
141
  content.appendChild(childWrapper);
131
142
 
132
- // Event binding - render children
143
+ // Render child INTO the wrapper
133
144
  if (typeof (child as any).render === 'function') {
134
145
  try {
146
+ // Pass the wrapper element directly
135
147
  const result = (child as any).render(childWrapper);
136
148
  if (result && typeof (result as any).then === 'function') {
137
149
  await result;
@@ -140,13 +152,17 @@ export class View extends Reactive {
140
152
  console.error(`View: Error rendering child ${i}:`, err);
141
153
  childWrapper.innerHTML = `<div style="color: #ff6b6b; padding: 1rem;">Error: ${(err as Error).message}</div>`;
142
154
  }
155
+ } else {
156
+ // If no render method, try to append directly
157
+ if (child instanceof HTMLElement) {
158
+ childWrapper.appendChild(child);
159
+ }
143
160
  }
144
161
  }
145
162
 
146
163
  view.appendChild(content);
147
164
  container.appendChild(view);
148
165
 
149
- this.emit('rendered');
150
166
  return this;
151
167
  }
152
168
 
@@ -158,17 +174,17 @@ export class View extends Reactive {
158
174
  throw new Error('View.renderTo: Invalid component - not an object');
159
175
  }
160
176
 
161
- if (!juxComponent._componentId || typeof juxComponent._componentId !== 'string') {
162
- throw new Error('View.renderTo: Invalid component - missing _componentId (not a Jux component)');
177
+ if (!juxComponent._id || typeof juxComponent._id !== 'string') {
178
+ throw new Error('View.renderTo: Invalid component - missing _id (not a Jux component)');
163
179
  }
164
180
 
165
- return this.render(`#${juxComponent._componentId}`);
181
+ return this.render(`#${juxComponent._id}`);
166
182
  }
167
183
  }
168
184
 
169
185
  /**
170
186
  * Factory helper
171
187
  */
172
- export function view(componentId: string, options: ViewOptions = {}): View {
173
- return new View(componentId, options);
188
+ export function view(id: string, options: ViewOptions = {}): View {
189
+ return new View(id, options);
174
190
  }
@@ -0,0 +1,284 @@
1
+ /**
2
+ * Write - Simple content writer with no component tracking
3
+ * Perfect for quick HTML output without ID management
4
+ */
5
+
6
+ export interface WriteOptions {
7
+ tagType?: string;
8
+ className?: string;
9
+ style?: string;
10
+ attributes?: Record<string, string>;
11
+ html?: boolean; // If true, treat content as HTML; if false, treat as text
12
+ }
13
+
14
+ /**
15
+ * Write content directly to a target element
16
+ *
17
+ * Usage:
18
+ * // Write text (defaults to body)
19
+ * jux.write('Hello World!').render();
20
+ *
21
+ * // Write to specific target
22
+ * jux.write('Content').render('#container');
23
+ *
24
+ * // Write HTML
25
+ * jux.write('<strong>Bold text</strong>', { html: true }).render('#container');
26
+ *
27
+ * // Write with styling
28
+ * jux.write('Styled text', {
29
+ * tagType: 'p',
30
+ * className: 'highlight',
31
+ * style: 'color: red;'
32
+ * }).render('#container');
33
+ *
34
+ * // Append multiple writes
35
+ * jux.write('First').render('#container');
36
+ * jux.write('Second').render('#container');
37
+ */
38
+ export class Write {
39
+ private content: string;
40
+ private options: WriteOptions;
41
+
42
+ constructor(content: string, options: WriteOptions = {}) {
43
+ this.content = content;
44
+ this.options = {
45
+ tagType: 'div',
46
+ className: '',
47
+ style: '',
48
+ attributes: {},
49
+ html: false,
50
+ ...options
51
+ };
52
+ }
53
+
54
+ /**
55
+ * Render content to target element
56
+ * Falls back to body if no target specified
57
+ */
58
+ render(targetSelector?: string): this {
59
+ // Default to body if no target
60
+ const selector = targetSelector || 'body';
61
+ const target = document.querySelector(selector);
62
+
63
+ if (!target || !(target instanceof HTMLElement)) {
64
+ console.warn(`Write: Target element "${selector}" not found`);
65
+ return this;
66
+ }
67
+
68
+ const element = document.createElement(this.options.tagType!);
69
+
70
+ // Set content (text or HTML)
71
+ if (this.options.html) {
72
+ element.innerHTML = this.content;
73
+ } else {
74
+ element.textContent = this.content;
75
+ }
76
+
77
+ // Apply className
78
+ if (this.options.className) {
79
+ element.className = this.options.className;
80
+ }
81
+
82
+ // Apply inline styles
83
+ if (this.options.style) {
84
+ element.setAttribute('style', this.options.style);
85
+ }
86
+
87
+ // Apply custom attributes
88
+ if (this.options.attributes) {
89
+ Object.entries(this.options.attributes).forEach(([key, value]) => {
90
+ element.setAttribute(key, value);
91
+ });
92
+ }
93
+
94
+ // Append to target
95
+ target.appendChild(element);
96
+
97
+ return this;
98
+ }
99
+
100
+ /**
101
+ * Replace target content (clear first, then render)
102
+ */
103
+ replace(targetSelector?: string): this {
104
+ const selector = targetSelector || 'body';
105
+ const target = document.querySelector(selector);
106
+
107
+ if (!target || !(target instanceof HTMLElement)) {
108
+ console.warn(`Write: Target element "${selector}" not found`);
109
+ return this;
110
+ }
111
+
112
+ // Clear existing content
113
+ target.innerHTML = '';
114
+
115
+ // Render new content
116
+ return this.render(selector);
117
+ }
118
+
119
+ /**
120
+ * Render before target element
121
+ */
122
+ before(targetSelector: string): this {
123
+ const target = document.querySelector(targetSelector);
124
+
125
+ if (!target || !(target instanceof HTMLElement)) {
126
+ console.warn(`Write: Target element "${targetSelector}" not found`);
127
+ return this;
128
+ }
129
+
130
+ const element = document.createElement(this.options.tagType!);
131
+
132
+ if (this.options.html) {
133
+ element.innerHTML = this.content;
134
+ } else {
135
+ element.textContent = this.content;
136
+ }
137
+
138
+ if (this.options.className) {
139
+ element.className = this.options.className;
140
+ }
141
+
142
+ if (this.options.style) {
143
+ element.setAttribute('style', this.options.style);
144
+ }
145
+
146
+ if (this.options.attributes) {
147
+ Object.entries(this.options.attributes).forEach(([key, value]) => {
148
+ element.setAttribute(key, value);
149
+ });
150
+ }
151
+
152
+ // Insert before target
153
+ target.parentNode?.insertBefore(element, target);
154
+
155
+ return this;
156
+ }
157
+
158
+ /**
159
+ * Render after target element
160
+ */
161
+ after(targetSelector: string): this {
162
+ const target = document.querySelector(targetSelector);
163
+
164
+ if (!target || !(target instanceof HTMLElement)) {
165
+ console.warn(`Write: Target element "${targetSelector}" not found`);
166
+ return this;
167
+ }
168
+
169
+ const element = document.createElement(this.options.tagType!);
170
+
171
+ if (this.options.html) {
172
+ element.innerHTML = this.content;
173
+ } else {
174
+ element.textContent = this.content;
175
+ }
176
+
177
+ if (this.options.className) {
178
+ element.className = this.options.className;
179
+ }
180
+
181
+ if (this.options.style) {
182
+ element.setAttribute('style', this.options.style);
183
+ }
184
+
185
+ if (this.options.attributes) {
186
+ Object.entries(this.options.attributes).forEach(([key, value]) => {
187
+ element.setAttribute(key, value);
188
+ });
189
+ }
190
+
191
+ // Insert after target
192
+ target.parentNode?.insertBefore(element, target.nextSibling);
193
+
194
+ return this;
195
+ }
196
+
197
+ /**
198
+ * Prepend to target (insert as first child)
199
+ */
200
+ prepend(targetSelector?: string): this {
201
+ const selector = targetSelector || 'body';
202
+ const target = document.querySelector(selector);
203
+
204
+ if (!target || !(target instanceof HTMLElement)) {
205
+ console.warn(`Write: Target element "${selector}" not found`);
206
+ return this;
207
+ }
208
+
209
+ const element = document.createElement(this.options.tagType!);
210
+
211
+ if (this.options.html) {
212
+ element.innerHTML = this.content;
213
+ } else {
214
+ element.textContent = this.content;
215
+ }
216
+
217
+ if (this.options.className) {
218
+ element.className = this.options.className;
219
+ }
220
+
221
+ if (this.options.style) {
222
+ element.setAttribute('style', this.options.style);
223
+ }
224
+
225
+ if (this.options.attributes) {
226
+ Object.entries(this.options.attributes).forEach(([key, value]) => {
227
+ element.setAttribute(key, value);
228
+ });
229
+ }
230
+
231
+ // Prepend to target
232
+ target.insertBefore(element, target.firstChild);
233
+
234
+ return this;
235
+ }
236
+
237
+ /**
238
+ * Append to target (alias for render)
239
+ */
240
+ append(targetSelector?: string): this {
241
+ return this.render(targetSelector);
242
+ }
243
+ }
244
+
245
+ /**
246
+ * Factory function for quick writing
247
+ */
248
+ export function write(content: string, options: WriteOptions = {}): Write {
249
+ return new Write(content, options);
250
+ }
251
+
252
+ /**
253
+ * Shorthand helpers
254
+ */
255
+
256
+ // Write text (alias)
257
+ export function writeText(content: string, options: Omit<WriteOptions, 'html'> = {}): Write {
258
+ return new Write(content, { ...options, html: false });
259
+ }
260
+
261
+ // Write HTML (alias)
262
+ export function writeHtml(content: string, options: Omit<WriteOptions, 'html'> = {}): Write {
263
+ return new Write(content, { ...options, html: true });
264
+ }
265
+
266
+ // Write paragraph
267
+ export function writeParagraph(content: string, options: Omit<WriteOptions, 'tagType'> = {}): Write {
268
+ return new Write(content, { ...options, tagType: 'p' });
269
+ }
270
+
271
+ // Write heading
272
+ export function writeHeading(content: string, level: 1 | 2 | 3 | 4 | 5 | 6 = 2, options: Omit<WriteOptions, 'tagType'> = {}): Write {
273
+ return new Write(content, { ...options, tagType: `h${level}` });
274
+ }
275
+
276
+ // Write span
277
+ export function writeSpan(content: string, options: Omit<WriteOptions, 'tagType'> = {}): Write {
278
+ return new Write(content, { ...options, tagType: 'span' });
279
+ }
280
+
281
+ // Write div (explicit)
282
+ export function writeDiv(content: string, options: Omit<WriteOptions, 'tagType'> = {}): Write {
283
+ return new Write(content, { ...options, tagType: 'div' });
284
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Global types for JUX projects
3
+ * This file provides IntelliSense in all .jux files
4
+ */
5
+
6
+ import type { JuxAPI } from './jux.js';
7
+ import type { code } from './components/code.js';
8
+
9
+ declare global {
10
+ /**
11
+ * The main JUX API - available in all .jux files
12
+ */
13
+ const jux: JuxAPI;
14
+
15
+ /**
16
+ * Code component factory - available globally
17
+ */
18
+ const code: typeof code;
19
+ }
20
+
21
+ export {};