juxscript 1.0.61 → 1.0.63
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 +161 -293
- package/docs/v2comps/HEADLESS.md +83 -0
- package/docs/v2comps/ISOMORPHISM.md +10 -0
- package/juxconfig.example.js +63 -58
- package/lib/componentsv2/base/BaseEngine.js +258 -0
- package/lib/componentsv2/base/BaseEngine.js.map +1 -0
- package/lib/componentsv2/base/BaseEngine.ts +303 -0
- package/lib/componentsv2/base/BaseSkin.js +108 -0
- package/lib/componentsv2/base/BaseSkin.js.map +1 -0
- package/lib/componentsv2/base/BaseSkin.ts +137 -0
- package/lib/componentsv2/base/GlobalBus.js +56 -0
- package/lib/componentsv2/base/GlobalBus.js.map +1 -0
- package/lib/componentsv2/base/GlobalBus.ts +60 -0
- package/lib/componentsv2/base/State.js +68 -0
- package/lib/componentsv2/base/State.js.map +1 -0
- package/lib/componentsv2/base/State.ts +62 -0
- package/lib/componentsv2/grid/component.js +41 -0
- package/lib/componentsv2/grid/component.js.map +1 -0
- package/lib/componentsv2/grid/component.ts +67 -0
- package/lib/componentsv2/grid/engine.js +73 -0
- package/lib/componentsv2/grid/engine.js.map +1 -0
- package/lib/componentsv2/grid/engine.ts +110 -0
- package/lib/componentsv2/grid/skin.js +95 -0
- package/lib/componentsv2/grid/skin.js.map +1 -0
- package/lib/componentsv2/grid/skin.ts +105 -0
- package/lib/componentsv2/grid/structure.css +58 -0
- package/lib/componentsv2/index.js +218 -0
- package/lib/componentsv2/index.js.map +1 -0
- package/lib/componentsv2/index.ts +253 -0
- package/lib/componentsv2/input/component.js +21 -0
- package/lib/componentsv2/input/component.js.map +1 -0
- package/lib/componentsv2/input/component.ts +28 -0
- package/lib/componentsv2/input/engine.js +50 -0
- package/lib/componentsv2/input/engine.js.map +1 -0
- package/lib/componentsv2/input/engine.ts +76 -0
- package/lib/componentsv2/input/skin.js +91 -0
- package/lib/componentsv2/input/skin.js.map +1 -0
- package/lib/componentsv2/input/skin.ts +91 -0
- package/lib/componentsv2/input/structure.css +47 -0
- package/lib/componentsv2/list/component.js +83 -0
- package/lib/componentsv2/list/component.js.map +1 -0
- package/lib/componentsv2/list/component.ts +97 -0
- package/lib/componentsv2/list/engine.js +261 -0
- package/lib/componentsv2/list/engine.js.map +1 -0
- package/lib/componentsv2/list/engine.ts +345 -0
- package/lib/componentsv2/list/skin.js +343 -0
- package/lib/componentsv2/list/skin.js.map +1 -0
- package/lib/componentsv2/list/skin.ts +367 -0
- package/lib/componentsv2/list/structure.css +359 -0
- package/lib/componentsv2/plugins/ClientSQLitePlugin.js +130 -0
- package/lib/componentsv2/plugins/ClientSQLitePlugin.js.map +1 -0
- package/lib/componentsv2/plugins/ClientSQLitePlugin.ts +154 -0
- package/lib/componentsv2/plugins/IndexedDBPlugin.js +75 -0
- package/lib/componentsv2/plugins/IndexedDBPlugin.js.map +1 -0
- package/lib/componentsv2/plugins/IndexedDBPlugin.ts +96 -0
- package/lib/componentsv2/plugins/LocalStoragePlugin.js +65 -0
- package/lib/componentsv2/plugins/LocalStoragePlugin.js.map +1 -0
- package/lib/componentsv2/plugins/LocalStoragePlugin.ts +86 -0
- package/lib/componentsv2/plugins/ServerSQLitePlugin.js +70 -0
- package/lib/componentsv2/plugins/ServerSQLitePlugin.js.map +1 -0
- package/lib/componentsv2/plugins/ServerSQLitePlugin.ts +99 -0
- package/lib/componentsv2/stubs/ComponentComposition.ts.stub +32 -0
- package/lib/componentsv2/stubs/ComponentEngine.ts.stub +36 -0
- package/lib/componentsv2/stubs/ComponentSkin.ts.stub +34 -0
- package/lib/componentsv2/stubs/ComponentStructure.css.stub +13 -0
- package/lib/componentsv2/tools/CreateSkin.js +62 -0
- package/lib/componentsv2/tools/DocSpam.js +134 -0
- package/lib/componentsv2/tools/FluencyAudit.js +141 -0
- package/lib/componentsv2/tools/OptionsAudit.js +177 -0
- package/lib/componentsv2/tools/Scaffold.js +140 -0
- package/lib/utils/fetch.js +428 -0
- package/lib/utils/fetch.js.map +1 -0
- package/machinery/build.js +2 -1
- package/machinery/compiler.js +200 -37
- package/machinery/config.js +93 -6
- package/machinery/diagnose.js +72 -0
- package/machinery/jux-module-pattern.md +118 -0
- package/machinery/server.js +23 -7
- package/machinery/verifier.js +143 -0
- package/machinery/watcher.js +53 -64
- package/package.json +11 -2
- package/lib/components/alert.ts +0 -200
- package/lib/components/app.ts +0 -258
- package/lib/components/badge.ts +0 -101
- package/lib/components/base/BaseComponent.ts +0 -417
- package/lib/components/base/FormInput.ts +0 -227
- package/lib/components/button.ts +0 -178
- package/lib/components/card.ts +0 -173
- package/lib/components/chart.ts +0 -231
- package/lib/components/checkbox.ts +0 -242
- package/lib/components/code.ts +0 -123
- package/lib/components/container.ts +0 -140
- package/lib/components/data.ts +0 -135
- package/lib/components/datepicker.ts +0 -234
- package/lib/components/dialog.ts +0 -172
- package/lib/components/divider.ts +0 -100
- package/lib/components/dropdown.ts +0 -186
- package/lib/components/element.ts +0 -267
- package/lib/components/error-handler.ts +0 -285
- package/lib/components/fileupload.ts +0 -309
- package/lib/components/grid.ts +0 -291
- package/lib/components/guard.ts +0 -92
- package/lib/components/heading.ts +0 -96
- package/lib/components/helpers.ts +0 -41
- package/lib/components/hero.ts +0 -224
- package/lib/components/icon.ts +0 -160
- package/lib/components/icons.ts +0 -175
- package/lib/components/include.ts +0 -440
- package/lib/components/input.ts +0 -457
- package/lib/components/list.ts +0 -419
- package/lib/components/loading.ts +0 -100
- package/lib/components/menu.ts +0 -260
- package/lib/components/modal.ts +0 -239
- package/lib/components/nav.ts +0 -257
- package/lib/components/paragraph.ts +0 -97
- package/lib/components/progress.ts +0 -139
- package/lib/components/radio.ts +0 -278
- package/lib/components/req.ts +0 -302
- package/lib/components/script.ts +0 -43
- package/lib/components/select.ts +0 -252
- package/lib/components/sidebar.ts +0 -167
- package/lib/components/style.ts +0 -43
- package/lib/components/switch.ts +0 -246
- package/lib/components/table.ts +0 -1249
- package/lib/components/tabs.ts +0 -250
- package/lib/components/theme-toggle.ts +0 -300
- package/lib/components/token-calculator.ts +0 -313
- package/lib/components/tooltip.ts +0 -144
- package/lib/components/view.ts +0 -190
- package/lib/components/write.ts +0 -272
- package/lib/jux.ts +0 -365
- package/lib/layouts/default.css +0 -260
- package/lib/layouts/figma.css +0 -334
- package/lib/reactivity/state.ts +0 -78
- package/machinery/bundleAssets.js +0 -0
- package/machinery/bundleJux.js +0 -0
- package/machinery/bundleVendors.js +0 -0
- package/presets/default/all.jux +0 -343
- package/presets/default/index.jux +0 -90
- package/presets/default/layout.css +0 -1612
- package/presets/default/layout.jux +0 -55
|
@@ -1,313 +0,0 @@
|
|
|
1
|
-
import { getOrCreateContainer } from './helpers.js';
|
|
2
|
-
import { ErrorHandler } from './error-handler.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Token Calculator component - Compare framework token costs
|
|
6
|
-
* Estimates AI token usage between JUX vs traditional frameworks (React, Vue, etc.)
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export interface TokenCalculatorOptions {
|
|
10
|
-
juxLines?: number;
|
|
11
|
-
multipliers?: {
|
|
12
|
-
react: number;
|
|
13
|
-
vue: number;
|
|
14
|
-
angular: number;
|
|
15
|
-
svelte: number;
|
|
16
|
-
};
|
|
17
|
-
tokensPerLine?: number;
|
|
18
|
-
showComparison?: boolean;
|
|
19
|
-
animated?: boolean;
|
|
20
|
-
style?: string;
|
|
21
|
-
class?: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface TokenEstimate {
|
|
25
|
-
framework: string;
|
|
26
|
-
lines: number;
|
|
27
|
-
tokens: number;
|
|
28
|
-
percentage: number;
|
|
29
|
-
savings: number;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export class TokenCalculator {
|
|
33
|
-
state: {
|
|
34
|
-
juxLines: number;
|
|
35
|
-
multipliers: Record<string, number>;
|
|
36
|
-
tokensPerLine: number;
|
|
37
|
-
showComparison: boolean;
|
|
38
|
-
animated: boolean;
|
|
39
|
-
style: string;
|
|
40
|
-
class: string;
|
|
41
|
-
};
|
|
42
|
-
container: HTMLElement | null = null;
|
|
43
|
-
_id: string;
|
|
44
|
-
id: string;
|
|
45
|
-
private animationFrame?: number;
|
|
46
|
-
|
|
47
|
-
constructor(id: string, options: TokenCalculatorOptions = {}) {
|
|
48
|
-
this._id = id;
|
|
49
|
-
this.id = id;
|
|
50
|
-
|
|
51
|
-
// Default multipliers based on typical framework overhead
|
|
52
|
-
const defaultMultipliers = {
|
|
53
|
-
react: 3.5, // JSX + component boilerplate + imports
|
|
54
|
-
vue: 3.2, // SFC (template + script + style) + composition API
|
|
55
|
-
angular: 4.0, // TypeScript decorators + templates + modules
|
|
56
|
-
svelte: 2.8 // Less overhead but still has markup
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
this.state = {
|
|
60
|
-
juxLines: options.juxLines ?? 0,
|
|
61
|
-
multipliers: { ...defaultMultipliers, ...options.multipliers },
|
|
62
|
-
tokensPerLine: options.tokensPerLine ?? 4, // Average tokens per line of code
|
|
63
|
-
showComparison: options.showComparison ?? true,
|
|
64
|
-
animated: options.animated ?? true,
|
|
65
|
-
style: options.style ?? '',
|
|
66
|
-
class: options.class ?? ''
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/* -------------------------
|
|
71
|
-
* Calculation Methods
|
|
72
|
-
* ------------------------- */
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Calculate token estimates for all frameworks
|
|
76
|
-
*/
|
|
77
|
-
calculate(): TokenEstimate[] {
|
|
78
|
-
const { juxLines, multipliers, tokensPerLine } = this.state;
|
|
79
|
-
const juxTokens = juxLines * tokensPerLine;
|
|
80
|
-
|
|
81
|
-
const estimates: TokenEstimate[] = [
|
|
82
|
-
{
|
|
83
|
-
framework: 'JUX',
|
|
84
|
-
lines: juxLines,
|
|
85
|
-
tokens: juxTokens,
|
|
86
|
-
percentage: 100,
|
|
87
|
-
savings: 0
|
|
88
|
-
}
|
|
89
|
-
];
|
|
90
|
-
|
|
91
|
-
// Calculate for each framework
|
|
92
|
-
Object.entries(multipliers).forEach(([framework, multiplier]) => {
|
|
93
|
-
const lines = Math.round(juxLines * multiplier);
|
|
94
|
-
const tokens = lines * tokensPerLine;
|
|
95
|
-
const percentage = Math.round((juxTokens / tokens) * 100);
|
|
96
|
-
const savings = tokens - juxTokens;
|
|
97
|
-
|
|
98
|
-
estimates.push({
|
|
99
|
-
framework: framework.charAt(0).toUpperCase() + framework.slice(1),
|
|
100
|
-
lines,
|
|
101
|
-
tokens,
|
|
102
|
-
percentage,
|
|
103
|
-
savings
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
return estimates;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Get summary statistics
|
|
112
|
-
*/
|
|
113
|
-
getSummary() {
|
|
114
|
-
const estimates = this.calculate();
|
|
115
|
-
const jux = estimates[0];
|
|
116
|
-
const others = estimates.slice(1);
|
|
117
|
-
|
|
118
|
-
const avgSavings = others.reduce((sum, e) => sum + e.savings, 0) / others.length;
|
|
119
|
-
const maxSavings = Math.max(...others.map(e => e.savings));
|
|
120
|
-
const avgReduction = others.reduce((sum, e) => sum + (100 - e.percentage), 0) / others.length;
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
juxTokens: jux.tokens,
|
|
124
|
-
averageTokenSavings: Math.round(avgSavings),
|
|
125
|
-
maxTokenSavings: Math.round(maxSavings),
|
|
126
|
-
averageReduction: Math.round(avgReduction),
|
|
127
|
-
estimates
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/* -------------------------
|
|
132
|
-
* Fluent API
|
|
133
|
-
* ------------------------- */
|
|
134
|
-
|
|
135
|
-
lines(value: number): this {
|
|
136
|
-
this.state.juxLines = value;
|
|
137
|
-
this._updateDOM();
|
|
138
|
-
return this;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
multiplier(framework: string, value: number): this {
|
|
142
|
-
this.state.multipliers[framework] = value;
|
|
143
|
-
this._updateDOM();
|
|
144
|
-
return this;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
tokensPerLine(value: number): this {
|
|
148
|
-
this.state.tokensPerLine = value;
|
|
149
|
-
this._updateDOM();
|
|
150
|
-
return this;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
showComparison(value: boolean): this {
|
|
154
|
-
this.state.showComparison = value;
|
|
155
|
-
this._updateDOM();
|
|
156
|
-
return this;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
animated(value: boolean): this {
|
|
160
|
-
this.state.animated = value;
|
|
161
|
-
return this;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
style(value: string): this {
|
|
165
|
-
this.state.style = value;
|
|
166
|
-
return this;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
class(value: string): this {
|
|
170
|
-
this.state.class = value;
|
|
171
|
-
return this;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/* -------------------------
|
|
175
|
-
* DOM Methods
|
|
176
|
-
* ------------------------- */
|
|
177
|
-
|
|
178
|
-
private _updateDOM(): void {
|
|
179
|
-
if (!this.container) return;
|
|
180
|
-
|
|
181
|
-
const wrapper = this.container.querySelector(`#${this._id}`);
|
|
182
|
-
if (!wrapper) return;
|
|
183
|
-
|
|
184
|
-
const content = wrapper.querySelector('.jux-token-calculator-content');
|
|
185
|
-
if (content) {
|
|
186
|
-
content.innerHTML = this._buildContent();
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
private _buildContent(): string {
|
|
192
|
-
const summary = this.getSummary();
|
|
193
|
-
const { estimates } = summary;
|
|
194
|
-
|
|
195
|
-
return `
|
|
196
|
-
${this.state.showComparison ? `
|
|
197
|
-
<div class="jux-token-details">
|
|
198
|
-
<table class="jux-token-table">
|
|
199
|
-
<thead>
|
|
200
|
-
<tr>
|
|
201
|
-
<th>Framework</th>
|
|
202
|
-
<th>Lines</th>
|
|
203
|
-
<th>Tokens</th>
|
|
204
|
-
<th>vs JUX</th>
|
|
205
|
-
<th>Savings</th>
|
|
206
|
-
</tr>
|
|
207
|
-
</thead>
|
|
208
|
-
<tbody>
|
|
209
|
-
${estimates.map(est => `
|
|
210
|
-
<tr class="${est.framework === 'JUX' ? 'jux-token-row-highlight' : ''}">
|
|
211
|
-
<td><strong>${est.framework}</strong></td>
|
|
212
|
-
<td>${est.lines.toLocaleString()}</td>
|
|
213
|
-
<td>${est.tokens.toLocaleString()}</td>
|
|
214
|
-
<td>${est.percentage}%</td>
|
|
215
|
-
<td>${est.savings > 0 ? '+' + est.savings.toLocaleString() : '—'}</td>
|
|
216
|
-
</tr>
|
|
217
|
-
`).join('')}
|
|
218
|
-
</tbody>
|
|
219
|
-
</table>
|
|
220
|
-
</div>
|
|
221
|
-
` : ''}
|
|
222
|
-
`;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
private _buildComparisonBar(estimate: TokenEstimate): string {
|
|
226
|
-
const isJux = estimate.framework === 'JUX';
|
|
227
|
-
const barClass = isJux ? 'jux-token-bar-jux' : 'jux-token-bar-other';
|
|
228
|
-
|
|
229
|
-
// Calculate bar width relative to largest value
|
|
230
|
-
const maxTokens = Math.max(...this.calculate().map(e => e.tokens));
|
|
231
|
-
const widthPercent = (estimate.tokens / maxTokens) * 100;
|
|
232
|
-
|
|
233
|
-
return `
|
|
234
|
-
<div class="jux-token-bar-row">
|
|
235
|
-
<div class="jux-token-bar-label">${estimate.framework}</div>
|
|
236
|
-
<div class="jux-token-bar-container">
|
|
237
|
-
<div
|
|
238
|
-
class="jux-token-bar ${barClass}"
|
|
239
|
-
style="width: 0%"
|
|
240
|
-
data-width="${widthPercent}"
|
|
241
|
-
>
|
|
242
|
-
<span class="jux-token-bar-value">${estimate.tokens.toLocaleString()} tokens</span>
|
|
243
|
-
</div>
|
|
244
|
-
</div>
|
|
245
|
-
</div>
|
|
246
|
-
`;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
/* -------------------------
|
|
252
|
-
* Render
|
|
253
|
-
* ------------------------- */
|
|
254
|
-
|
|
255
|
-
render(targetId?: string): this {
|
|
256
|
-
let container: HTMLElement;
|
|
257
|
-
|
|
258
|
-
if (targetId) {
|
|
259
|
-
const target = document.querySelector(targetId);
|
|
260
|
-
if (!target || !(target instanceof HTMLElement)) {
|
|
261
|
-
throw new Error(`TokenCalculator: Target element "${targetId}" not found`);
|
|
262
|
-
}
|
|
263
|
-
container = target;
|
|
264
|
-
} else {
|
|
265
|
-
container = getOrCreateContainer(this._id);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
this.container = container;
|
|
269
|
-
|
|
270
|
-
const wrapper = document.createElement('div');
|
|
271
|
-
wrapper.id = this._id;
|
|
272
|
-
wrapper.className = `jux-token-calculator ${this.state.class}`.trim();
|
|
273
|
-
|
|
274
|
-
if (this.state.style) {
|
|
275
|
-
wrapper.setAttribute('style', this.state.style);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
wrapper.innerHTML = `
|
|
279
|
-
<div class="jux-token-calculator-header">
|
|
280
|
-
<h3>Token Usage Calculator</h3>
|
|
281
|
-
</div>
|
|
282
|
-
<div class="jux-token-calculator-content">
|
|
283
|
-
${this._buildContent()}
|
|
284
|
-
</div>
|
|
285
|
-
`;
|
|
286
|
-
|
|
287
|
-
container.appendChild(wrapper);
|
|
288
|
-
return this;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Destroy component and cleanup animations
|
|
293
|
-
*/
|
|
294
|
-
destroy(): void {
|
|
295
|
-
if (this.animationFrame) {
|
|
296
|
-
cancelAnimationFrame(this.animationFrame);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
if (this.container) {
|
|
300
|
-
const wrapper = this.container.querySelector(`#${this._id}`);
|
|
301
|
-
if (wrapper) {
|
|
302
|
-
wrapper.remove();
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Factory function
|
|
310
|
-
*/
|
|
311
|
-
export function tokenCalculator(id: string, options: TokenCalculatorOptions = {}): TokenCalculator {
|
|
312
|
-
return new TokenCalculator(id, options);
|
|
313
|
-
}
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import { BaseComponent } from './base/BaseComponent.js';
|
|
2
|
-
|
|
3
|
-
// Event definitions
|
|
4
|
-
const TRIGGER_EVENTS = [] as const;
|
|
5
|
-
const CALLBACK_EVENTS = [] as const;
|
|
6
|
-
|
|
7
|
-
export interface TooltipOptions {
|
|
8
|
-
text?: string;
|
|
9
|
-
position?: 'top' | 'bottom' | 'left' | 'right';
|
|
10
|
-
style?: string;
|
|
11
|
-
class?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
type TooltipState = {
|
|
15
|
-
text: string;
|
|
16
|
-
position: string;
|
|
17
|
-
style: string;
|
|
18
|
-
class: string;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export class Tooltip extends BaseComponent<TooltipState> {
|
|
22
|
-
private _tooltip: HTMLElement | null = null;
|
|
23
|
-
private _target: HTMLElement | null = null;
|
|
24
|
-
|
|
25
|
-
constructor(id: string, options: TooltipOptions = {}) {
|
|
26
|
-
super(id, {
|
|
27
|
-
text: options.text ?? '',
|
|
28
|
-
position: options.position ?? 'top',
|
|
29
|
-
style: options.style ?? '',
|
|
30
|
-
class: options.class ?? ''
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
protected getTriggerEvents(): readonly string[] {
|
|
35
|
-
return TRIGGER_EVENTS;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
protected getCallbackEvents(): readonly string[] {
|
|
39
|
-
return CALLBACK_EVENTS;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/* ═════════════════════════════════════════════════════════════════
|
|
43
|
-
* FLUENT API
|
|
44
|
-
* ═════════════════════════════════════════════════════════════════ */
|
|
45
|
-
|
|
46
|
-
text(value: string): this {
|
|
47
|
-
this.state.text = value;
|
|
48
|
-
return this;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
position(value: 'top' | 'bottom' | 'left' | 'right'): this {
|
|
52
|
-
this.state.position = value;
|
|
53
|
-
return this;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
attachTo(target: HTMLElement | null): this {
|
|
57
|
-
if (!target) return this;
|
|
58
|
-
|
|
59
|
-
this._target = target;
|
|
60
|
-
this._setupTooltip();
|
|
61
|
-
return this;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/* ═════════════════════════════════════════════════════════════════
|
|
65
|
-
* RENDER
|
|
66
|
-
* ═════════════════════════════════════════════════════════════════ */
|
|
67
|
-
|
|
68
|
-
private _setupTooltip(): void {
|
|
69
|
-
if (!this._target) return;
|
|
70
|
-
|
|
71
|
-
const { text, position, style, class: className } = this.state;
|
|
72
|
-
|
|
73
|
-
// Create tooltip element
|
|
74
|
-
const tooltip = document.createElement('div');
|
|
75
|
-
tooltip.className = `jux-tooltip jux-tooltip-${position}`;
|
|
76
|
-
tooltip.id = this._id;
|
|
77
|
-
if (className) tooltip.className += ` ${className}`;
|
|
78
|
-
if (style) tooltip.setAttribute('style', style);
|
|
79
|
-
tooltip.textContent = text;
|
|
80
|
-
tooltip.style.display = 'none';
|
|
81
|
-
this._tooltip = tooltip;
|
|
82
|
-
|
|
83
|
-
document.body.appendChild(tooltip);
|
|
84
|
-
|
|
85
|
-
// Show on hover
|
|
86
|
-
this._target.addEventListener('mouseenter', () => {
|
|
87
|
-
this._show();
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
this._target.addEventListener('mouseleave', () => {
|
|
91
|
-
this._hide();
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
private _show(): void {
|
|
97
|
-
if (!this._tooltip || !this._target) return;
|
|
98
|
-
|
|
99
|
-
const targetRect = this._target.getBoundingClientRect();
|
|
100
|
-
const tooltipRect = this._tooltip.getBoundingClientRect();
|
|
101
|
-
|
|
102
|
-
let top = 0;
|
|
103
|
-
let left = 0;
|
|
104
|
-
|
|
105
|
-
switch (this.state.position) {
|
|
106
|
-
case 'top':
|
|
107
|
-
top = targetRect.top - tooltipRect.height - 8;
|
|
108
|
-
left = targetRect.left + (targetRect.width - tooltipRect.width) / 2;
|
|
109
|
-
break;
|
|
110
|
-
case 'bottom':
|
|
111
|
-
top = targetRect.bottom + 8;
|
|
112
|
-
left = targetRect.left + (targetRect.width - tooltipRect.width) / 2;
|
|
113
|
-
break;
|
|
114
|
-
case 'left':
|
|
115
|
-
top = targetRect.top + (targetRect.height - tooltipRect.height) / 2;
|
|
116
|
-
left = targetRect.left - tooltipRect.width - 8;
|
|
117
|
-
break;
|
|
118
|
-
case 'right':
|
|
119
|
-
top = targetRect.top + (targetRect.height - tooltipRect.height) / 2;
|
|
120
|
-
left = targetRect.right + 8;
|
|
121
|
-
break;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
this._tooltip.style.top = `${top}px`;
|
|
125
|
-
this._tooltip.style.left = `${left}px`;
|
|
126
|
-
this._tooltip.style.display = 'block';
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
private _hide(): void {
|
|
130
|
-
if (this._tooltip) {
|
|
131
|
-
this._tooltip.style.display = 'none';
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
render(targetId?: string): this {
|
|
136
|
-
// Tooltips don't render to containers, they attach to targets
|
|
137
|
-
console.warn('Tooltip: Use .attachTo(element) instead of .render()');
|
|
138
|
-
return this;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export function tooltip(id: string, options: TooltipOptions = {}): Tooltip {
|
|
143
|
-
return new Tooltip(id, options);
|
|
144
|
-
}
|
package/lib/components/view.ts
DELETED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import { getOrCreateContainer } from './helpers.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* View component options
|
|
5
|
-
*/
|
|
6
|
-
export interface ViewOptions {
|
|
7
|
-
title?: string;
|
|
8
|
-
description?: string;
|
|
9
|
-
children?: any[];
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* View component state
|
|
14
|
-
*/
|
|
15
|
-
type ViewState = {
|
|
16
|
-
title: string;
|
|
17
|
-
description: string;
|
|
18
|
-
children: any[];
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* View component - container for organizing page content
|
|
23
|
-
*
|
|
24
|
-
* Usage:
|
|
25
|
-
* const view = jux.view('myView', {
|
|
26
|
-
* title: 'Dashboard',
|
|
27
|
-
* description: 'Main dashboard view'
|
|
28
|
-
* });
|
|
29
|
-
* view.add(component1);
|
|
30
|
-
* await view.render();
|
|
31
|
-
*/
|
|
32
|
-
export class View {
|
|
33
|
-
state: ViewState;
|
|
34
|
-
container: HTMLElement | null = null;
|
|
35
|
-
_id: string;
|
|
36
|
-
id: string;
|
|
37
|
-
|
|
38
|
-
constructor(id: string, options: ViewOptions = {}) {
|
|
39
|
-
this._id = id;
|
|
40
|
-
this.id = id;
|
|
41
|
-
|
|
42
|
-
this.state = {
|
|
43
|
-
title: options.title ?? '',
|
|
44
|
-
description: options.description ?? '',
|
|
45
|
-
children: Array.isArray(options.children) ? [...options.children] : []
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/* -------------------------
|
|
50
|
-
* Fluent API
|
|
51
|
-
* ------------------------- */
|
|
52
|
-
|
|
53
|
-
title(value: string): this {
|
|
54
|
-
this.state.title = value;
|
|
55
|
-
return this;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
description(value: string): this {
|
|
59
|
-
this.state.description = value;
|
|
60
|
-
return this;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
add(component: any | any[]): this {
|
|
64
|
-
if (Array.isArray(component)) {
|
|
65
|
-
this.state.children.push(...component);
|
|
66
|
-
} else {
|
|
67
|
-
this.state.children.push(component);
|
|
68
|
-
}
|
|
69
|
-
return this;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/* -------------------------
|
|
73
|
-
* Render
|
|
74
|
-
* ------------------------- */
|
|
75
|
-
|
|
76
|
-
async render(targetId?: string): Promise<this> {
|
|
77
|
-
let container: HTMLElement;
|
|
78
|
-
|
|
79
|
-
if (targetId) {
|
|
80
|
-
const target = document.querySelector(targetId);
|
|
81
|
-
if (!target || !(target instanceof HTMLElement)) {
|
|
82
|
-
throw new Error(`View: Target element "${targetId}" not found`);
|
|
83
|
-
}
|
|
84
|
-
container = target;
|
|
85
|
-
} else {
|
|
86
|
-
container = getOrCreateContainer(this._id);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
this.container = container;
|
|
90
|
-
const { title, description, children } = this.state;
|
|
91
|
-
|
|
92
|
-
const view = document.createElement('div');
|
|
93
|
-
view.className = 'jux-view';
|
|
94
|
-
view.id = this._id;
|
|
95
|
-
|
|
96
|
-
// View header
|
|
97
|
-
if (title || description) {
|
|
98
|
-
const header = document.createElement('div');
|
|
99
|
-
header.className = 'jux-view-header';
|
|
100
|
-
|
|
101
|
-
if (title) {
|
|
102
|
-
const titleEl = document.createElement('h1');
|
|
103
|
-
titleEl.className = 'jux-view-title';
|
|
104
|
-
titleEl.textContent = title;
|
|
105
|
-
header.appendChild(titleEl);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (description) {
|
|
109
|
-
const descEl = document.createElement('p');
|
|
110
|
-
descEl.className = 'jux-view-description';
|
|
111
|
-
descEl.textContent = description;
|
|
112
|
-
header.appendChild(descEl);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
view.appendChild(header);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// View content
|
|
119
|
-
const content = document.createElement('div');
|
|
120
|
-
content.className = 'jux-view-content';
|
|
121
|
-
|
|
122
|
-
// Render children
|
|
123
|
-
for (let i = 0; i < children.length; i++) {
|
|
124
|
-
const child = children[i];
|
|
125
|
-
if (!child) continue;
|
|
126
|
-
|
|
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
|
-
}
|
|
136
|
-
|
|
137
|
-
// Create wrapper for child
|
|
138
|
-
const childWrapper = document.createElement('div');
|
|
139
|
-
childWrapper.className = 'jux-view-item';
|
|
140
|
-
childWrapper.id = `${childId}-wrapper`;
|
|
141
|
-
content.appendChild(childWrapper);
|
|
142
|
-
|
|
143
|
-
// Render child INTO the wrapper
|
|
144
|
-
if (typeof (child as any).render === 'function') {
|
|
145
|
-
try {
|
|
146
|
-
// Pass the wrapper element directly
|
|
147
|
-
const result = (child as any).render(childWrapper);
|
|
148
|
-
if (result && typeof (result as any).then === 'function') {
|
|
149
|
-
await result;
|
|
150
|
-
}
|
|
151
|
-
} catch (err) {
|
|
152
|
-
console.error(`View: Error rendering child ${i}:`, err);
|
|
153
|
-
childWrapper.innerHTML = `<div style="color: #ff6b6b; padding: 1rem;">Error: ${(err as Error).message}</div>`;
|
|
154
|
-
}
|
|
155
|
-
} else {
|
|
156
|
-
// If no render method, try to append directly
|
|
157
|
-
if (child instanceof HTMLElement) {
|
|
158
|
-
childWrapper.appendChild(child);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
view.appendChild(content);
|
|
164
|
-
container.appendChild(view);
|
|
165
|
-
|
|
166
|
-
return this;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Render to another Jux component's container
|
|
171
|
-
*/
|
|
172
|
-
async renderTo(juxComponent: any): Promise<this> {
|
|
173
|
-
if (!juxComponent || typeof juxComponent !== 'object') {
|
|
174
|
-
throw new Error('View.renderTo: Invalid component - not an object');
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (!juxComponent._id || typeof juxComponent._id !== 'string') {
|
|
178
|
-
throw new Error('View.renderTo: Invalid component - missing _id (not a Jux component)');
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return this.render(`#${juxComponent._id}`);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Factory helper
|
|
187
|
-
*/
|
|
188
|
-
export function view(id: string, options: ViewOptions = {}): View {
|
|
189
|
-
return new View(id, options);
|
|
190
|
-
}
|