juxscript 1.0.18 → 1.0.20
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/lib/components/alert.ts +124 -128
- package/lib/components/areachart.ts +169 -287
- package/lib/components/areachartsmooth.ts +2 -2
- package/lib/components/badge.ts +63 -72
- package/lib/components/barchart.ts +120 -48
- package/lib/components/button.ts +99 -101
- package/lib/components/card.ts +97 -121
- package/lib/components/chart-types.ts +159 -0
- package/lib/components/chart-utils.ts +160 -0
- package/lib/components/chart.ts +628 -48
- package/lib/components/checkbox.ts +137 -51
- package/lib/components/code.ts +89 -75
- package/lib/components/container.ts +1 -1
- package/lib/components/datepicker.ts +93 -78
- package/lib/components/dialog.ts +163 -130
- package/lib/components/divider.ts +111 -193
- package/lib/components/docs-data.json +711 -264
- package/lib/components/doughnutchart.ts +125 -57
- package/lib/components/dropdown.ts +172 -85
- package/lib/components/element.ts +66 -61
- package/lib/components/fileupload.ts +142 -171
- package/lib/components/heading.ts +64 -21
- package/lib/components/hero.ts +109 -34
- package/lib/components/icon.ts +247 -0
- package/lib/components/icons.ts +174 -0
- package/lib/components/include.ts +77 -2
- package/lib/components/input.ts +174 -125
- package/lib/components/list.ts +120 -79
- package/lib/components/menu.ts +97 -2
- package/lib/components/modal.ts +144 -63
- package/lib/components/nav.ts +153 -52
- package/lib/components/paragraph.ts +78 -28
- package/lib/components/progress.ts +83 -107
- package/lib/components/radio.ts +151 -52
- package/lib/components/select.ts +110 -102
- package/lib/components/sidebar.ts +148 -105
- package/lib/components/switch.ts +124 -125
- package/lib/components/table.ts +214 -137
- package/lib/components/tabs.ts +194 -113
- package/lib/components/theme-toggle.ts +38 -7
- package/lib/components/tooltip.ts +207 -47
- package/lib/jux.ts +24 -5
- package/lib/reactivity/state.ts +13 -299
- package/package.json +1 -2
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { getOrCreateContainer } from './helpers.js';
|
|
2
|
+
import { State } from '../reactivity/state.js';
|
|
2
3
|
|
|
3
|
-
/**
|
|
4
|
-
* Tooltip component options
|
|
5
|
-
*/
|
|
6
4
|
export interface TooltipOptions {
|
|
7
5
|
text?: string;
|
|
8
6
|
position?: 'top' | 'bottom' | 'left' | 'right';
|
|
@@ -11,38 +9,32 @@ export interface TooltipOptions {
|
|
|
11
9
|
class?: string;
|
|
12
10
|
}
|
|
13
11
|
|
|
14
|
-
/**
|
|
15
|
-
* Tooltip component state
|
|
16
|
-
*/
|
|
17
12
|
type TooltipState = {
|
|
18
13
|
text: string;
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
content?: string;
|
|
15
|
+
position: 'top' | 'bottom' | 'left' | 'right';
|
|
16
|
+
trigger: 'hover' | 'click' | 'focus';
|
|
21
17
|
style: string;
|
|
22
18
|
class: string;
|
|
19
|
+
isVisible: boolean;
|
|
23
20
|
};
|
|
24
21
|
|
|
25
|
-
/**
|
|
26
|
-
* Tooltip component - Contextual help on hover
|
|
27
|
-
*
|
|
28
|
-
* Usage:
|
|
29
|
-
* // Attach to existing element
|
|
30
|
-
* jux.tooltip('help-tip', {
|
|
31
|
-
* text: 'This is helpful information',
|
|
32
|
-
* position: 'top'
|
|
33
|
-
* }).attachTo('#help-icon');
|
|
34
|
-
*
|
|
35
|
-
* // Or render with content
|
|
36
|
-
* jux.button('info').label('ℹ️').render('#app');
|
|
37
|
-
* jux.tooltip('info-tip').text('More info').attachTo('#info');
|
|
38
|
-
*/
|
|
39
22
|
export class Tooltip {
|
|
40
23
|
state: TooltipState;
|
|
41
24
|
container: HTMLElement | null = null;
|
|
42
25
|
_id: string;
|
|
43
26
|
id: string;
|
|
44
|
-
private _targetElement: HTMLElement | null = null;
|
|
45
27
|
private _tooltipElement: HTMLElement | null = null;
|
|
28
|
+
private _targetElement: HTMLElement | null = null;
|
|
29
|
+
|
|
30
|
+
// CRITICAL: Store bind/sync instructions for deferred wiring
|
|
31
|
+
private _bindings: Array<{ event: string, handler: Function }> = [];
|
|
32
|
+
private _syncBindings: Array<{
|
|
33
|
+
property: string,
|
|
34
|
+
stateObj: State<any>,
|
|
35
|
+
toState?: Function,
|
|
36
|
+
toComponent?: Function
|
|
37
|
+
}> = [];
|
|
46
38
|
|
|
47
39
|
constructor(id: string, options: TooltipOptions = {}) {
|
|
48
40
|
this._id = id;
|
|
@@ -53,7 +45,8 @@ export class Tooltip {
|
|
|
53
45
|
position: options.position ?? 'top',
|
|
54
46
|
trigger: options.trigger ?? 'hover',
|
|
55
47
|
style: options.style ?? '',
|
|
56
|
-
class: options.class ?? ''
|
|
48
|
+
class: options.class ?? '',
|
|
49
|
+
isVisible: false
|
|
57
50
|
};
|
|
58
51
|
}
|
|
59
52
|
|
|
@@ -63,6 +56,9 @@ export class Tooltip {
|
|
|
63
56
|
|
|
64
57
|
text(value: string): this {
|
|
65
58
|
this.state.text = value;
|
|
59
|
+
if (this._tooltipElement) {
|
|
60
|
+
this._tooltipElement.textContent = value;
|
|
61
|
+
}
|
|
66
62
|
return this;
|
|
67
63
|
}
|
|
68
64
|
|
|
@@ -86,9 +82,18 @@ export class Tooltip {
|
|
|
86
82
|
return this;
|
|
87
83
|
}
|
|
88
84
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
85
|
+
bind(event: string, handler: Function): this {
|
|
86
|
+
this._bindings.push({ event, handler });
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
sync(property: string, stateObj: State<any>, toState?: Function, toComponent?: Function): this {
|
|
91
|
+
if (!stateObj || typeof stateObj.subscribe !== 'function') {
|
|
92
|
+
throw new Error(`Tooltip.sync: Expected a State object for property "${property}"`);
|
|
93
|
+
}
|
|
94
|
+
this._syncBindings.push({ property, stateObj, toState, toComponent });
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
92
97
|
|
|
93
98
|
/**
|
|
94
99
|
* Attach tooltip to an element
|
|
@@ -97,20 +102,16 @@ export class Tooltip {
|
|
|
97
102
|
let targetElement: HTMLElement | null = null;
|
|
98
103
|
|
|
99
104
|
if (typeof target === 'string') {
|
|
100
|
-
// String selector
|
|
101
105
|
const el = document.querySelector(target);
|
|
102
106
|
if (!el || !(el instanceof HTMLElement)) {
|
|
103
107
|
throw new Error(`Tooltip: Target element "${target}" not found`);
|
|
104
108
|
}
|
|
105
109
|
targetElement = el;
|
|
106
110
|
} else if (target instanceof HTMLElement) {
|
|
107
|
-
// Direct HTMLElement
|
|
108
111
|
targetElement = target;
|
|
109
112
|
} else if (target && target.container) {
|
|
110
|
-
// Jux component with container
|
|
111
113
|
targetElement = target.container;
|
|
112
114
|
} else if (target && target._id) {
|
|
113
|
-
// Jux component with _id
|
|
114
115
|
const el = document.getElementById(target._id);
|
|
115
116
|
if (!el) {
|
|
116
117
|
throw new Error(`Tooltip: Target element with id "${target._id}" not found`);
|
|
@@ -128,16 +129,18 @@ export class Tooltip {
|
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
show(): void {
|
|
131
|
-
if (this._tooltipElement)
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
if (!this._tooltipElement || !this._targetElement) return;
|
|
133
|
+
|
|
134
|
+
this.state.isVisible = true;
|
|
135
|
+
this._tooltipElement.classList.add('jux-tooltip-visible');
|
|
136
|
+
this._positionTooltip();
|
|
135
137
|
}
|
|
136
138
|
|
|
137
139
|
hide(): void {
|
|
138
|
-
if (this._tooltipElement)
|
|
139
|
-
|
|
140
|
-
|
|
140
|
+
if (!this._tooltipElement) return;
|
|
141
|
+
|
|
142
|
+
this.state.isVisible = false;
|
|
143
|
+
this._tooltipElement.classList.remove('jux-tooltip-visible');
|
|
141
144
|
}
|
|
142
145
|
|
|
143
146
|
/* -------------------------
|
|
@@ -149,8 +152,7 @@ export class Tooltip {
|
|
|
149
152
|
|
|
150
153
|
const tooltip = document.createElement('div');
|
|
151
154
|
tooltip.className = `jux-tooltip jux-tooltip-${position}`;
|
|
152
|
-
tooltip.id = this._id
|
|
153
|
-
tooltip.setAttribute('role', 'tooltip');
|
|
155
|
+
tooltip.id = `${this._id}-tooltip`;
|
|
154
156
|
tooltip.textContent = text;
|
|
155
157
|
|
|
156
158
|
if (className) {
|
|
@@ -163,6 +165,8 @@ export class Tooltip {
|
|
|
163
165
|
|
|
164
166
|
document.body.appendChild(tooltip);
|
|
165
167
|
this._tooltipElement = tooltip;
|
|
168
|
+
|
|
169
|
+
this._injectDefaultStyles();
|
|
166
170
|
}
|
|
167
171
|
|
|
168
172
|
private _attachEventListeners(): void {
|
|
@@ -175,7 +179,7 @@ export class Tooltip {
|
|
|
175
179
|
this._targetElement.addEventListener('mouseleave', () => this.hide());
|
|
176
180
|
} else if (trigger === 'click') {
|
|
177
181
|
this._targetElement.addEventListener('click', () => {
|
|
178
|
-
if (this.
|
|
182
|
+
if (this.state.isVisible) {
|
|
179
183
|
this.hide();
|
|
180
184
|
} else {
|
|
181
185
|
this.show();
|
|
@@ -185,6 +189,21 @@ export class Tooltip {
|
|
|
185
189
|
this._targetElement.addEventListener('focus', () => this.show());
|
|
186
190
|
this._targetElement.addEventListener('blur', () => this.hide());
|
|
187
191
|
}
|
|
192
|
+
|
|
193
|
+
// Wire up sync bindings
|
|
194
|
+
this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
|
|
195
|
+
if (property === 'text') {
|
|
196
|
+
const transformToComponent = toComponent || ((v: any) => String(v));
|
|
197
|
+
|
|
198
|
+
stateObj.subscribe((val: any) => {
|
|
199
|
+
const transformed = transformToComponent(val);
|
|
200
|
+
if (this._tooltipElement) {
|
|
201
|
+
this._tooltipElement.textContent = transformed;
|
|
202
|
+
}
|
|
203
|
+
this.state.text = transformed;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
});
|
|
188
207
|
}
|
|
189
208
|
|
|
190
209
|
private _positionTooltip(): void {
|
|
@@ -220,22 +239,163 @@ export class Tooltip {
|
|
|
220
239
|
this._tooltipElement.style.left = `${left + window.scrollX}px`;
|
|
221
240
|
}
|
|
222
241
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
242
|
+
private _injectDefaultStyles(): void {
|
|
243
|
+
const styleId = 'jux-tooltip-styles';
|
|
244
|
+
if (document.getElementById(styleId)) return;
|
|
245
|
+
|
|
246
|
+
const style = document.createElement('style');
|
|
247
|
+
style.id = styleId;
|
|
248
|
+
style.textContent = `
|
|
249
|
+
.jux-tooltip {
|
|
250
|
+
position: absolute;
|
|
251
|
+
background: #1f2937;
|
|
252
|
+
color: white;
|
|
253
|
+
padding: 8px 12px;
|
|
254
|
+
border-radius: 6px;
|
|
255
|
+
font-size: 13px;
|
|
256
|
+
white-space: nowrap;
|
|
257
|
+
z-index: 10000;
|
|
258
|
+
pointer-events: none;
|
|
259
|
+
opacity: 0;
|
|
260
|
+
transition: opacity 0.2s;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.jux-tooltip-visible {
|
|
264
|
+
opacity: 1;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.jux-tooltip-top::after {
|
|
268
|
+
content: '';
|
|
269
|
+
position: absolute;
|
|
270
|
+
top: 100%;
|
|
271
|
+
left: 50%;
|
|
272
|
+
transform: translateX(-50%);
|
|
273
|
+
border: 5px solid transparent;
|
|
274
|
+
border-top-color: #1f2937;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.jux-tooltip-bottom::after {
|
|
278
|
+
content: '';
|
|
279
|
+
position: absolute;
|
|
280
|
+
bottom: 100%;
|
|
281
|
+
left: 50%;
|
|
282
|
+
transform: translateX(-50%);
|
|
283
|
+
border: 5px solid transparent;
|
|
284
|
+
border-bottom-color: #1f2937;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.jux-tooltip-left::after {
|
|
288
|
+
content: '';
|
|
289
|
+
position: absolute;
|
|
290
|
+
left: 100%;
|
|
291
|
+
top: 50%;
|
|
292
|
+
transform: translateY(-50%);
|
|
293
|
+
border: 5px solid transparent;
|
|
294
|
+
border-left-color: #1f2937;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.jux-tooltip-right::after {
|
|
298
|
+
content: '';
|
|
299
|
+
position: absolute;
|
|
300
|
+
right: 100%;
|
|
301
|
+
top: 50%;
|
|
302
|
+
transform: translateY(-50%);
|
|
303
|
+
border: 5px solid transparent;
|
|
304
|
+
border-right-color: #1f2937;
|
|
305
|
+
}
|
|
306
|
+
`;
|
|
307
|
+
document.head.appendChild(style);
|
|
308
|
+
}
|
|
226
309
|
|
|
227
310
|
render(targetId?: string): this {
|
|
311
|
+
// === 1. SETUP: Get or create container ===
|
|
312
|
+
let container: HTMLElement;
|
|
228
313
|
if (targetId) {
|
|
229
|
-
|
|
314
|
+
const target = document.querySelector(targetId);
|
|
315
|
+
if (!target || !(target instanceof HTMLElement)) {
|
|
316
|
+
throw new Error(`Tooltip: Target "${targetId}" not found`);
|
|
317
|
+
}
|
|
318
|
+
container = target;
|
|
319
|
+
} else {
|
|
320
|
+
container = getOrCreateContainer(this._id);
|
|
230
321
|
}
|
|
231
|
-
|
|
322
|
+
this.container = container;
|
|
323
|
+
|
|
324
|
+
// === 2. PREPARE: Destructure state ===
|
|
325
|
+
const { text, content, position, trigger, style, class: className } = this.state;
|
|
326
|
+
|
|
327
|
+
// === 3. BUILD: Create DOM elements ===
|
|
328
|
+
const wrapper = document.createElement('div');
|
|
329
|
+
wrapper.className = 'jux-tooltip-wrapper';
|
|
330
|
+
wrapper.id = this._id;
|
|
331
|
+
if (className) wrapper.className += ` ${className}`;
|
|
332
|
+
if (style) wrapper.setAttribute('style', style);
|
|
333
|
+
|
|
334
|
+
const contentEl = document.createElement('div');
|
|
335
|
+
contentEl.className = 'jux-tooltip-content';
|
|
336
|
+
contentEl.innerHTML = content;
|
|
337
|
+
wrapper.appendChild(contentEl);
|
|
338
|
+
|
|
339
|
+
const tooltip = document.createElement('div');
|
|
340
|
+
tooltip.className = `jux-tooltip jux-tooltip-${position}`;
|
|
341
|
+
tooltip.id = `${this._id}-tooltip`;
|
|
342
|
+
tooltip.textContent = text;
|
|
343
|
+
tooltip.style.display = 'none';
|
|
344
|
+
wrapper.appendChild(tooltip);
|
|
345
|
+
|
|
346
|
+
// === 4. WIRE: Attach event listeners and sync bindings ===
|
|
347
|
+
|
|
348
|
+
// Tooltip trigger behavior
|
|
349
|
+
if (trigger === 'hover') {
|
|
350
|
+
wrapper.addEventListener('mouseenter', () => {
|
|
351
|
+
tooltip.style.display = 'block';
|
|
352
|
+
});
|
|
353
|
+
wrapper.addEventListener('mouseleave', () => {
|
|
354
|
+
tooltip.style.display = 'none';
|
|
355
|
+
});
|
|
356
|
+
} else if (trigger === 'click') {
|
|
357
|
+
wrapper.addEventListener('click', () => {
|
|
358
|
+
tooltip.style.display = tooltip.style.display === 'none' ? 'block' : 'none';
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Wire custom bindings from .bind() calls
|
|
363
|
+
this._bindings.forEach(({ event, handler }) => {
|
|
364
|
+
wrapper.addEventListener(event, handler as EventListener);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
// Wire sync bindings from .sync() calls
|
|
368
|
+
this._syncBindings.forEach(({ property, stateObj, toState, toComponent }) => {
|
|
369
|
+
if (property === 'text') {
|
|
370
|
+
const transformToComponent = toComponent || ((v: any) => String(v));
|
|
371
|
+
|
|
372
|
+
stateObj.subscribe((val: any) => {
|
|
373
|
+
const transformed = transformToComponent(val);
|
|
374
|
+
tooltip.textContent = transformed;
|
|
375
|
+
this.state.text = transformed;
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
else if (property === 'content') {
|
|
379
|
+
const transformToComponent = toComponent || ((v: any) => String(v));
|
|
380
|
+
|
|
381
|
+
stateObj.subscribe((val: any) => {
|
|
382
|
+
const transformed = transformToComponent(val);
|
|
383
|
+
contentEl.innerHTML = transformed;
|
|
384
|
+
this.state.content = transformed;
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
// === 5. RENDER: Append to DOM and finalize ===
|
|
390
|
+
container.appendChild(wrapper);
|
|
391
|
+
return this;
|
|
232
392
|
}
|
|
233
393
|
|
|
234
394
|
renderTo(juxComponent: any): this {
|
|
235
395
|
if (!juxComponent?._id) {
|
|
236
396
|
throw new Error('Tooltip.renderTo: Invalid component');
|
|
237
397
|
}
|
|
238
|
-
return this.
|
|
398
|
+
return this.render(`#${juxComponent._id}`);
|
|
239
399
|
}
|
|
240
400
|
}
|
|
241
401
|
|
package/lib/jux.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { state } from './reactivity/state.js';
|
|
7
7
|
import { Data, data } from './components/data.js';
|
|
8
|
-
import { table, Table, type TableOptions
|
|
8
|
+
import { table, Table, type TableOptions } from './components/table.js';
|
|
9
9
|
import { hero, Hero, type HeroOptions } from './components/hero.js';
|
|
10
10
|
import { card, Card, type CardOptions } from './components/card.js';
|
|
11
11
|
import { button, Button, type ButtonOptions } from './components/button.js';
|
|
@@ -55,6 +55,12 @@ import { areachartsmooth, AreaChartSmooth, type AreaChartSmoothOptions, AreaChar
|
|
|
55
55
|
import { doughnutchart, DoughnutChart, type DoughnutChartOptions, type DoughnutChartDataPoint } from './components/doughnutchart.js';
|
|
56
56
|
import { kpicard, KPICard, type KPICardOptions } from './components/kpicard.js';
|
|
57
57
|
import { divider, Divider, type DividerOptions } from './components/divider.js';
|
|
58
|
+
import { icon as iconComponent, Icon, type IconOptions } from './components/icon.js';
|
|
59
|
+
import { renderIcon, renderEmoji } from './components/icons.js';
|
|
60
|
+
|
|
61
|
+
// NEW: Export chart utilities
|
|
62
|
+
export * from './components/chart-types.js';
|
|
63
|
+
export * from './components/chart-utils.js';
|
|
58
64
|
|
|
59
65
|
/* -------------------------
|
|
60
66
|
* Type Exports
|
|
@@ -79,7 +85,6 @@ export type {
|
|
|
79
85
|
NavItem,
|
|
80
86
|
ViewOptions,
|
|
81
87
|
TableOptions,
|
|
82
|
-
TableColumn,
|
|
83
88
|
ChartOptions,
|
|
84
89
|
CodeOptions,
|
|
85
90
|
InputOptions,
|
|
@@ -118,7 +123,8 @@ export type {
|
|
|
118
123
|
DoughnutChartOptions,
|
|
119
124
|
DoughnutChartDataPoint,
|
|
120
125
|
KPICardOptions,
|
|
121
|
-
DividerOptions
|
|
126
|
+
DividerOptions,
|
|
127
|
+
IconOptions
|
|
122
128
|
};
|
|
123
129
|
|
|
124
130
|
/* -------------------------
|
|
@@ -175,7 +181,8 @@ export {
|
|
|
175
181
|
AreaChartSmooth,
|
|
176
182
|
DoughnutChart,
|
|
177
183
|
KPICard,
|
|
178
|
-
Divider
|
|
184
|
+
Divider,
|
|
185
|
+
Icon
|
|
179
186
|
};
|
|
180
187
|
|
|
181
188
|
/* -------------------------
|
|
@@ -188,6 +195,11 @@ export interface JuxAPI {
|
|
|
188
195
|
param(name: string): string | null;
|
|
189
196
|
req: typeof req;
|
|
190
197
|
|
|
198
|
+
// Icon utilities
|
|
199
|
+
icon: typeof renderIcon;
|
|
200
|
+
emoji: typeof renderEmoji;
|
|
201
|
+
iconComponent: typeof iconComponent;
|
|
202
|
+
|
|
191
203
|
// Component factories
|
|
192
204
|
data: typeof data;
|
|
193
205
|
table: typeof table;
|
|
@@ -266,6 +278,11 @@ class Jux implements JuxAPI {
|
|
|
266
278
|
// Request utilities
|
|
267
279
|
req = req;
|
|
268
280
|
|
|
281
|
+
// Icon utilities
|
|
282
|
+
icon = renderIcon;
|
|
283
|
+
emoji = renderEmoji;
|
|
284
|
+
iconComponent = iconComponent;
|
|
285
|
+
|
|
269
286
|
// Component factory methods
|
|
270
287
|
data = data;
|
|
271
288
|
table = table;
|
|
@@ -335,5 +352,7 @@ export {
|
|
|
335
352
|
jux,
|
|
336
353
|
state,
|
|
337
354
|
req,
|
|
338
|
-
ErrorHandler
|
|
355
|
+
ErrorHandler,
|
|
356
|
+
renderIcon,
|
|
357
|
+
renderEmoji
|
|
339
358
|
};
|