juxscript 1.0.20 → 1.0.21
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/bin/cli.js +121 -72
- package/lib/components/alert.ts +143 -92
- package/lib/components/badge.ts +93 -94
- package/lib/components/base/BaseComponent.ts +397 -0
- package/lib/components/base/FormInput.ts +322 -0
- package/lib/components/button.ts +40 -131
- package/lib/components/card.ts +57 -79
- package/lib/components/charts/areachart.ts +315 -0
- package/lib/components/charts/barchart.ts +421 -0
- package/lib/components/charts/doughnutchart.ts +263 -0
- package/lib/components/charts/lib/BaseChart.ts +402 -0
- package/lib/components/{chart-types.ts → charts/lib/chart-types.ts} +1 -1
- package/lib/components/{chart-utils.ts → charts/lib/chart-utils.ts} +1 -1
- package/lib/components/{chart.ts → charts/lib/chart.ts} +3 -3
- package/lib/components/checkbox.ts +255 -204
- package/lib/components/code.ts +31 -78
- package/lib/components/container.ts +113 -130
- package/lib/components/data.ts +37 -5
- package/lib/components/datepicker.ts +180 -147
- package/lib/components/dialog.ts +218 -221
- package/lib/components/divider.ts +63 -87
- package/lib/components/docs-data.json +498 -2404
- package/lib/components/dropdown.ts +191 -236
- package/lib/components/element.ts +196 -145
- package/lib/components/fileupload.ts +253 -167
- package/lib/components/guard.ts +92 -0
- package/lib/components/heading.ts +31 -97
- package/lib/components/helpers.ts +13 -6
- package/lib/components/hero.ts +51 -114
- package/lib/components/icon.ts +33 -120
- package/lib/components/icons.ts +2 -1
- package/lib/components/include.ts +76 -3
- package/lib/components/input.ts +155 -407
- package/lib/components/kpicard.ts +16 -16
- package/lib/components/list.ts +358 -261
- package/lib/components/loading.ts +142 -211
- package/lib/components/menu.ts +63 -152
- package/lib/components/modal.ts +42 -129
- package/lib/components/nav.ts +79 -101
- package/lib/components/paragraph.ts +38 -102
- package/lib/components/progress.ts +108 -166
- package/lib/components/radio.ts +283 -234
- package/lib/components/script.ts +19 -87
- package/lib/components/select.ts +189 -199
- package/lib/components/sidebar.ts +110 -141
- package/lib/components/style.ts +19 -82
- package/lib/components/switch.ts +254 -183
- package/lib/components/table.ts +1078 -208
- package/lib/components/tabs.ts +42 -106
- package/lib/components/theme-toggle.ts +73 -165
- package/lib/components/tooltip.ts +85 -316
- package/lib/components/write.ts +108 -127
- package/lib/jux.ts +67 -41
- package/machinery/build.js +466 -0
- package/machinery/compiler.js +354 -105
- package/machinery/server.js +23 -100
- package/machinery/watcher.js +153 -130
- package/package.json +1 -1
- package/presets/base.css +1166 -0
- package/presets/notion.css +2 -1975
- package/lib/adapters/base-adapter.js +0 -35
- package/lib/adapters/index.js +0 -33
- package/lib/adapters/mysql-adapter.js +0 -65
- package/lib/adapters/postgres-adapter.js +0 -70
- package/lib/adapters/sqlite-adapter.js +0 -56
- package/lib/components/areachart.ts +0 -1128
- package/lib/components/areachartsmooth.ts +0 -1380
- package/lib/components/barchart.ts +0 -1322
- package/lib/components/doughnutchart.ts +0 -1259
- package/lib/components/footer.ts +0 -165
- package/lib/components/header.ts +0 -187
- package/lib/components/layout.ts +0 -239
- package/lib/components/main.ts +0 -137
- package/lib/layouts/default.jux +0 -8
- package/lib/layouts/figma.jux +0 -0
- /package/lib/{themes → components/charts/lib}/charts.js +0 -0
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { BaseComponent } from './base/BaseComponent.js';
|
|
2
|
+
|
|
3
|
+
// Event definitions
|
|
4
|
+
const TRIGGER_EVENTS = [] as const; // Element has no trigger events
|
|
5
|
+
const CALLBACK_EVENTS = [] as const; // Element has no callback events
|
|
3
6
|
|
|
4
7
|
export interface ElementOptions {
|
|
5
8
|
tagType?: string;
|
|
@@ -23,20 +26,11 @@ type ElementState = {
|
|
|
23
26
|
class: string;
|
|
24
27
|
};
|
|
25
28
|
|
|
26
|
-
export class Element {
|
|
27
|
-
|
|
28
|
-
container: HTMLElement | null = null;
|
|
29
|
-
_id: string;
|
|
30
|
-
id: string;
|
|
31
|
-
|
|
32
|
-
private _bindings: Array<{ event: string, handler: Function }> = [];
|
|
33
|
-
private _syncBindings: Array<{ property: string, stateObj: State<any>, transform?: Function }> = [];
|
|
29
|
+
export class Element extends BaseComponent<ElementState> {
|
|
30
|
+
private _element: HTMLElement | null = null;
|
|
34
31
|
|
|
35
32
|
constructor(id: string, options: ElementOptions = {}) {
|
|
36
|
-
|
|
37
|
-
this.id = id;
|
|
38
|
-
|
|
39
|
-
this.state = {
|
|
33
|
+
super(id, {
|
|
40
34
|
tagType: options.tagType ?? 'div',
|
|
41
35
|
className: options.className ?? '',
|
|
42
36
|
text: options.text ?? '',
|
|
@@ -45,164 +39,129 @@ export class Element {
|
|
|
45
39
|
styles: options.styles ?? {},
|
|
46
40
|
style: options.style ?? '',
|
|
47
41
|
class: options.class ?? ''
|
|
48
|
-
};
|
|
42
|
+
});
|
|
49
43
|
}
|
|
50
44
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
protected getTriggerEvents(): readonly string[] {
|
|
46
|
+
return TRIGGER_EVENTS;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
protected getCallbackEvents(): readonly string[] {
|
|
50
|
+
return CALLBACK_EVENTS;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
54
|
+
* FLUENT API
|
|
55
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
56
|
+
|
|
57
|
+
// ✅ Inherited from BaseComponent:
|
|
58
|
+
// - style(), class()
|
|
59
|
+
// - addClass(), removeClass(), toggleClass()
|
|
60
|
+
// - visible(), show(), hide(), toggleVisibility()
|
|
61
|
+
// - attr(), attrs(), removeAttr()
|
|
62
|
+
// - disabled(), enable(), disable()
|
|
63
|
+
// - loading()
|
|
64
|
+
// - focus(), blur()
|
|
65
|
+
// - remove()
|
|
66
|
+
// - bind(), sync(), renderTo()
|
|
54
67
|
|
|
55
|
-
/**
|
|
56
|
-
* Set the HTML tag type
|
|
57
|
-
*/
|
|
58
68
|
tagType(value: string): Element {
|
|
59
69
|
this.state.tagType = value;
|
|
60
70
|
return this;
|
|
61
71
|
}
|
|
62
72
|
|
|
63
|
-
/**
|
|
64
|
-
* Set the className
|
|
65
|
-
*/
|
|
66
73
|
className(value: string): Element {
|
|
67
74
|
this.state.className = value;
|
|
68
75
|
return this;
|
|
69
76
|
}
|
|
70
77
|
|
|
71
|
-
/**
|
|
72
|
-
* Add a class to existing classes
|
|
73
|
-
*/
|
|
74
|
-
addClass(value: string): Element {
|
|
75
|
-
const classes = this.state.className.split(' ').filter(c => c);
|
|
76
|
-
if (!classes.includes(value)) {
|
|
77
|
-
classes.push(value);
|
|
78
|
-
this.state.className = classes.join(' ');
|
|
79
|
-
}
|
|
80
|
-
return this;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Remove a class from existing classes
|
|
85
|
-
*/
|
|
86
|
-
removeClass(value: string): Element {
|
|
87
|
-
const classes = this.state.className.split(' ').filter(c => c && c !== value);
|
|
88
|
-
this.state.className = classes.join(' ');
|
|
89
|
-
return this;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Set text content (static)
|
|
94
|
-
*/
|
|
95
78
|
text(value: string): Element {
|
|
96
79
|
this.state.text = value;
|
|
97
80
|
this.state.innerHTML = '';
|
|
98
81
|
return this;
|
|
99
82
|
}
|
|
100
83
|
|
|
101
|
-
/**
|
|
102
|
-
* Set HTML content (static, clears text)
|
|
103
|
-
*/
|
|
104
84
|
innerHTML(value: string): Element {
|
|
105
85
|
this.state.innerHTML = value;
|
|
106
86
|
this.state.text = '';
|
|
107
87
|
return this;
|
|
108
88
|
}
|
|
109
89
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return this;
|
|
116
|
-
}
|
|
90
|
+
write(content: string, asHTML: boolean = false, newLine: boolean = false): Element {
|
|
91
|
+
if (!this._element) {
|
|
92
|
+
console.warn(`Element.write: Element "${this._id}" not yet rendered`);
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
117
95
|
|
|
118
|
-
|
|
119
|
-
* Set multiple attributes
|
|
120
|
-
*/
|
|
121
|
-
attrs(attributes: Record<string, string>): Element {
|
|
122
|
-
this.state.attributes = { ...this.state.attributes, ...attributes };
|
|
123
|
-
return this;
|
|
124
|
-
}
|
|
96
|
+
const contentWithBreak = newLine ? (asHTML ? content + '<br>' : content + '\n') : content;
|
|
125
97
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
style(propertyOrValue: string | Record<string, string>, value?: string): Element {
|
|
132
|
-
if (typeof propertyOrValue === 'string' && value !== undefined) {
|
|
133
|
-
// Called as .style('color', 'red')
|
|
134
|
-
this.state.styles = { ...this.state.styles, [propertyOrValue]: value };
|
|
135
|
-
} else if (typeof propertyOrValue === 'string') {
|
|
136
|
-
// Called as .style('color: red; background: blue')
|
|
137
|
-
this.state.style = propertyOrValue;
|
|
138
|
-
} else if (typeof propertyOrValue === 'object') {
|
|
139
|
-
// Called as .style({ color: 'red', background: 'blue' })
|
|
140
|
-
this.state.styles = { ...this.state.styles, ...propertyOrValue };
|
|
98
|
+
if (asHTML) {
|
|
99
|
+
this._element.insertAdjacentHTML('beforeend', contentWithBreak);
|
|
100
|
+
} else {
|
|
101
|
+
const textNode = document.createTextNode(contentWithBreak);
|
|
102
|
+
this._element.appendChild(textNode);
|
|
141
103
|
}
|
|
142
|
-
return this;
|
|
143
|
-
}
|
|
144
104
|
|
|
145
|
-
/**
|
|
146
|
-
* Set multiple styles as object
|
|
147
|
-
*/
|
|
148
|
-
styles(styles: Record<string, string>): Element {
|
|
149
|
-
this.state.styles = { ...this.state.styles, ...styles };
|
|
150
105
|
return this;
|
|
151
106
|
}
|
|
152
107
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
108
|
+
prepend(content: string, asHTML: boolean = false, newLine: boolean = false): Element {
|
|
109
|
+
if (!this._element) {
|
|
110
|
+
console.warn(`Element.prepend: Element "${this._id}" not yet rendered`);
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const contentWithBreak = newLine ? (asHTML ? content + '<br>' : content + '\n') : content;
|
|
115
|
+
|
|
116
|
+
if (asHTML) {
|
|
117
|
+
this._element.insertAdjacentHTML('afterbegin', contentWithBreak);
|
|
118
|
+
} else {
|
|
119
|
+
const textNode = document.createTextNode(contentWithBreak);
|
|
120
|
+
this._element.insertBefore(textNode, this._element.firstChild);
|
|
121
|
+
}
|
|
122
|
+
|
|
158
123
|
return this;
|
|
159
124
|
}
|
|
160
125
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
126
|
+
clear(): Element {
|
|
127
|
+
if (!this._element) {
|
|
128
|
+
console.warn(`Element.clear: Element "${this._id}" not yet rendered`);
|
|
129
|
+
return this;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
this._element.innerHTML = '';
|
|
166
133
|
return this;
|
|
167
134
|
}
|
|
168
135
|
|
|
169
136
|
/**
|
|
170
|
-
*
|
|
137
|
+
* Append child component or HTML element
|
|
171
138
|
*/
|
|
172
|
-
|
|
173
|
-
if (!
|
|
174
|
-
|
|
139
|
+
append(child: any): Element {
|
|
140
|
+
if (!this._element) {
|
|
141
|
+
console.warn(`Element.append: Element "${this._id}" not yet rendered`);
|
|
142
|
+
return this;
|
|
175
143
|
}
|
|
176
|
-
|
|
144
|
+
|
|
145
|
+
if (child instanceof HTMLElement) {
|
|
146
|
+
this._element.appendChild(child);
|
|
147
|
+
} else if (child?._element) {
|
|
148
|
+
// Another Jux component
|
|
149
|
+
this._element.appendChild(child._element);
|
|
150
|
+
} else if (typeof child === 'string') {
|
|
151
|
+
const div = document.createElement('div');
|
|
152
|
+
div.innerHTML = child;
|
|
153
|
+
this._element.appendChild(div.firstChild || div);
|
|
154
|
+
}
|
|
155
|
+
|
|
177
156
|
return this;
|
|
178
157
|
}
|
|
179
158
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
* ------------------------- */
|
|
159
|
+
render(targetId?: string): this {
|
|
160
|
+
const container = this._setupContainer(targetId);
|
|
183
161
|
|
|
184
|
-
/**
|
|
185
|
-
* Render the element to a target element
|
|
186
|
-
* @param targetId - CSS selector for target element (optional, defaults to body)
|
|
187
|
-
*/
|
|
188
|
-
render(targetId?: string): Element {
|
|
189
|
-
// === 1. SETUP: Get container ===
|
|
190
|
-
let container: HTMLElement;
|
|
191
|
-
if (targetId) {
|
|
192
|
-
const target = document.querySelector(targetId);
|
|
193
|
-
if (!target || !(target instanceof HTMLElement)) {
|
|
194
|
-
throw new Error(`Element: Target element "${targetId}" not found`);
|
|
195
|
-
}
|
|
196
|
-
container = target;
|
|
197
|
-
} else {
|
|
198
|
-
container = getOrCreateContainer(this._id);
|
|
199
|
-
}
|
|
200
|
-
this.container = container;
|
|
201
|
-
|
|
202
|
-
// === 2. PREPARE: Destructure state ===
|
|
203
162
|
const { tagType, className, text, innerHTML, attributes, styles, style, class: classValue } = this.state;
|
|
204
163
|
|
|
205
|
-
//
|
|
164
|
+
// Build element
|
|
206
165
|
const element = document.createElement(tagType);
|
|
207
166
|
element.id = this._id;
|
|
208
167
|
|
|
@@ -227,15 +186,15 @@ export class Element {
|
|
|
227
186
|
element.setAttribute('style', `${styleString}; ${style}`.trim());
|
|
228
187
|
}
|
|
229
188
|
|
|
230
|
-
//
|
|
189
|
+
// Wire events using inherited method
|
|
190
|
+
this._wireStandardEvents(element);
|
|
231
191
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
192
|
+
// Wire sync bindings
|
|
193
|
+
this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
|
|
194
|
+
const transform = toComponent || ((v: any) => v);
|
|
235
195
|
|
|
236
|
-
this._syncBindings.forEach(({ property, stateObj, transform }) => {
|
|
237
196
|
stateObj.subscribe((val: any) => {
|
|
238
|
-
const transformed = transform
|
|
197
|
+
const transformed = transform(val);
|
|
239
198
|
|
|
240
199
|
if (property === 'text') {
|
|
241
200
|
element.textContent = String(transformed);
|
|
@@ -250,27 +209,119 @@ export class Element {
|
|
|
250
209
|
});
|
|
251
210
|
});
|
|
252
211
|
|
|
253
|
-
// === 5. RENDER: Append to DOM ===
|
|
254
212
|
container.appendChild(element);
|
|
213
|
+
this._element = element;
|
|
214
|
+
|
|
215
|
+
// Inject base semantic styles if this is a semantic element
|
|
216
|
+
this._injectSemanticStyles();
|
|
255
217
|
|
|
256
218
|
return this;
|
|
257
219
|
}
|
|
258
220
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
221
|
+
private _injectSemanticStyles(): void {
|
|
222
|
+
const styleId = 'jux-semantic-styles';
|
|
223
|
+
if (document.getElementById(styleId)) return;
|
|
224
|
+
|
|
225
|
+
const style = document.createElement('style');
|
|
226
|
+
style.id = styleId;
|
|
227
|
+
style.textContent = `
|
|
228
|
+
/* Jux Semantic Element Base Styles */
|
|
229
|
+
.jux-header {
|
|
230
|
+
display: block;
|
|
231
|
+
width: 100%;
|
|
232
|
+
background: var(--jux-header-bg, #ffffff);
|
|
233
|
+
border-bottom: 1px solid var(--jux-border-color, #e5e7eb);
|
|
234
|
+
padding: var(--jux-header-padding, 1rem 2rem);
|
|
235
|
+
position: sticky;
|
|
236
|
+
top: 0;
|
|
237
|
+
z-index: 1000;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.jux-footer {
|
|
241
|
+
display: block;
|
|
242
|
+
width: 100%;
|
|
243
|
+
background: var(--jux-footer-bg, #f9fafb);
|
|
244
|
+
border-top: 1px solid var(--jux-border-color, #e5e7eb);
|
|
245
|
+
padding: var(--jux-footer-padding, 2rem);
|
|
246
|
+
margin-top: auto;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.jux-main {
|
|
250
|
+
display: block;
|
|
251
|
+
flex: 1;
|
|
252
|
+
width: 100%;
|
|
253
|
+
padding: var(--jux-main-padding, 2rem);
|
|
254
|
+
min-height: calc(100vh - 200px);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.jux-aside {
|
|
258
|
+
display: block;
|
|
259
|
+
background: var(--jux-aside-bg, #f9fafb);
|
|
260
|
+
border-right: 1px solid var(--jux-border-color, #e5e7eb);
|
|
261
|
+
padding: var(--jux-aside-padding, 1.5rem);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.jux-section {
|
|
265
|
+
display: block;
|
|
266
|
+
margin-bottom: var(--jux-section-margin, 2rem);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.jux-article {
|
|
270
|
+
display: block;
|
|
271
|
+
max-width: var(--jux-article-max-width, 65ch);
|
|
272
|
+
margin: 0 auto;
|
|
273
|
+
}
|
|
274
|
+
`;
|
|
275
|
+
document.head.appendChild(style);
|
|
271
276
|
}
|
|
272
277
|
}
|
|
273
278
|
|
|
274
279
|
export function element(id: string, options: ElementOptions = {}): Element {
|
|
275
280
|
return new Element(id, options);
|
|
276
|
-
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Semantic HTML element factories with default classes
|
|
284
|
+
export function header(id: string, options: Omit<ElementOptions, 'tagType'> = {}): Element {
|
|
285
|
+
const defaultClass = 'jux-header';
|
|
286
|
+
const mergedClass = options.class ? `${defaultClass} ${options.class}` : defaultClass;
|
|
287
|
+
return new Element(id, { ...options, tagType: 'header', class: mergedClass });
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export function footer(id: string, options: Omit<ElementOptions, 'tagType'> = {}): Element {
|
|
291
|
+
const defaultClass = 'jux-footer';
|
|
292
|
+
const mergedClass = options.class ? `${defaultClass} ${options.class}` : defaultClass;
|
|
293
|
+
return new Element(id, { ...options, tagType: 'footer', class: mergedClass });
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function main(id: string, options: Omit<ElementOptions, 'tagType'> = {}): Element {
|
|
297
|
+
const defaultClass = 'jux-main';
|
|
298
|
+
const mergedClass = options.class ? `${defaultClass} ${options.class}` : defaultClass;
|
|
299
|
+
return new Element(id, { ...options, tagType: 'main', class: mergedClass });
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function aside(id: string, options: Omit<ElementOptions, 'tagType'> = {}): Element {
|
|
303
|
+
const defaultClass = 'jux-aside';
|
|
304
|
+
const mergedClass = options.class ? `${defaultClass} ${options.class}` : defaultClass;
|
|
305
|
+
return new Element(id, { ...options, tagType: 'aside', class: mergedClass });
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export function section(id: string, options: Omit<ElementOptions, 'tagType'> = {}): Element {
|
|
309
|
+
const defaultClass = 'jux-section';
|
|
310
|
+
const mergedClass = options.class ? `${defaultClass} ${options.class}` : defaultClass;
|
|
311
|
+
return new Element(id, { ...options, tagType: 'section', class: mergedClass });
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export function article(id: string, options: Omit<ElementOptions, 'tagType'> = {}): Element {
|
|
315
|
+
const defaultClass = 'jux-article';
|
|
316
|
+
const mergedClass = options.class ? `${defaultClass} ${options.class}` : defaultClass;
|
|
317
|
+
return new Element(id, { ...options, tagType: 'article', class: mergedClass });
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function div(id: string, options: Omit<ElementOptions, 'tagType'> = {}): Element {
|
|
321
|
+
return new Element(id, { ...options, tagType: 'div' });
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export function span(id: string, options: Omit<ElementOptions, 'tagType'> = {}): Element {
|
|
325
|
+
return new Element(id, { ...options, tagType: 'span' });
|
|
326
|
+
}
|
|
327
|
+
|