juxscript 1.0.3 → 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/lib/components/view.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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
|
|
33
|
-
state
|
|
32
|
+
export class View {
|
|
33
|
+
state: ViewState;
|
|
34
34
|
container: HTMLElement | null = null;
|
|
35
|
+
_id: string;
|
|
36
|
+
id: string;
|
|
35
37
|
|
|
36
|
-
constructor(
|
|
37
|
-
|
|
38
|
-
this.
|
|
38
|
+
constructor(id: string, options: ViewOptions = {}) {
|
|
39
|
+
this._id = id;
|
|
40
|
+
this.id = id;
|
|
39
41
|
|
|
40
|
-
this.state =
|
|
42
|
+
this.state = {
|
|
41
43
|
title: options.title ?? '',
|
|
42
44
|
description: options.description ?? '',
|
|
43
|
-
children: Array.isArray(options.children) ? [...options.children] : []
|
|
44
|
-
}
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
//
|
|
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.
|
|
162
|
-
throw new Error('View.renderTo: Invalid component - missing
|
|
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.
|
|
181
|
+
return this.render(`#${juxComponent._id}`);
|
|
166
182
|
}
|
|
167
183
|
}
|
|
168
184
|
|
|
169
185
|
/**
|
|
170
186
|
* Factory helper
|
|
171
187
|
*/
|
|
172
|
-
export function view(
|
|
173
|
-
return new View(
|
|
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
|
+
}
|
package/lib/globals.d.ts
ADDED
|
@@ -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 {};
|