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
package/lib/reactivity/state.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Notifies subscribers when value changes
|
|
2
|
+
* Simple reactive state container
|
|
4
3
|
*/
|
|
5
4
|
export class State<T> {
|
|
6
5
|
private _value: T;
|
|
7
6
|
private _subscribers: Set<(value: T) => void> = new Set();
|
|
8
|
-
private _effects: Set<Effect> = new Set();
|
|
9
7
|
|
|
10
8
|
constructor(initialValue: T) {
|
|
11
9
|
this._value = initialValue;
|
|
@@ -22,342 +20,58 @@ export class State<T> {
|
|
|
22
20
|
* Set new value and notify subscribers
|
|
23
21
|
*/
|
|
24
22
|
set(newValue: T): void {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
this._notify();
|
|
28
|
-
this._runEffects();
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Update value using a function
|
|
34
|
-
*/
|
|
35
|
-
update(fn: (current: T) => T): void {
|
|
36
|
-
this.set(fn(this._value));
|
|
23
|
+
this._value = newValue;
|
|
24
|
+
this._notify();
|
|
37
25
|
}
|
|
38
26
|
|
|
39
27
|
/**
|
|
40
28
|
* Subscribe to value changes
|
|
41
|
-
* Returns unsubscribe function
|
|
42
29
|
*/
|
|
43
30
|
subscribe(callback: (value: T) => void): () => void {
|
|
44
31
|
this._subscribers.add(callback);
|
|
45
|
-
|
|
32
|
+
// Call immediately with current value
|
|
33
|
+
callback(this._value);
|
|
34
|
+
// Return unsubscribe function
|
|
46
35
|
return () => this._subscribers.delete(callback);
|
|
47
36
|
}
|
|
48
37
|
|
|
49
38
|
/**
|
|
50
|
-
* Notify all subscribers
|
|
39
|
+
* Notify all subscribers
|
|
51
40
|
*/
|
|
52
41
|
private _notify(): void {
|
|
53
42
|
this._subscribers.forEach(callback => callback(this._value));
|
|
54
43
|
}
|
|
55
44
|
|
|
56
45
|
/**
|
|
57
|
-
*
|
|
58
|
-
*/
|
|
59
|
-
private _runEffects(): void {
|
|
60
|
-
this._effects.forEach(effect => effect.run(this._value));
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/* -------------------------
|
|
64
|
-
* Higher-Order Effects
|
|
65
|
-
* ------------------------- */
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Bind visibility to element (v-show style)
|
|
69
|
-
* Toggles display: none on/off
|
|
70
|
-
* For animations, use bindClass() instead
|
|
71
|
-
*/
|
|
72
|
-
bindVisibility(elementId: string): () => void {
|
|
73
|
-
if (typeof this._value !== 'boolean') {
|
|
74
|
-
console.warn(`State.bindVisibility: State value must be boolean, got ${typeof this._value}`);
|
|
75
|
-
return () => { };
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const effect = new VisibilityEffect(elementId);
|
|
79
|
-
this._effects.add(effect);
|
|
80
|
-
effect.run(this._value); // Initial run
|
|
81
|
-
|
|
82
|
-
return () => this._effects.delete(effect);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Bind value property to element (for progress, input, range, etc.)
|
|
87
|
-
* @param elementId - DOM element ID
|
|
88
|
-
* @param transform - Optional transform function
|
|
89
|
-
*/
|
|
90
|
-
bindValue(elementId: string, transform?: (val: T) => number | string): () => void {
|
|
91
|
-
const effect = new ValueEffect(elementId, transform);
|
|
92
|
-
this._effects.add(effect);
|
|
93
|
-
effect.run(this._value);
|
|
94
|
-
|
|
95
|
-
return () => this._effects.delete(effect);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Bind text content to element
|
|
100
|
-
* @param elementId - DOM element ID
|
|
101
|
-
* @param transform - Optional transform function
|
|
46
|
+
* Helper methods for numeric state
|
|
102
47
|
*/
|
|
103
|
-
|
|
104
|
-
const effect = new TextEffect(elementId, transform);
|
|
105
|
-
this._effects.add(effect);
|
|
106
|
-
effect.run(this._value);
|
|
107
|
-
|
|
108
|
-
return () => this._effects.delete(effect);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Bind attribute to element
|
|
113
|
-
* @param elementId - DOM element ID
|
|
114
|
-
* @param attrName - Attribute name
|
|
115
|
-
* @param transform - Optional transform function
|
|
116
|
-
*/
|
|
117
|
-
bindAttr(elementId: string, attrName: string, transform?: (val: T) => string): () => void {
|
|
118
|
-
const effect = new AttrEffect(elementId, attrName, transform);
|
|
119
|
-
this._effects.add(effect);
|
|
120
|
-
effect.run(this._value);
|
|
121
|
-
|
|
122
|
-
return () => this._effects.delete(effect);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Bind CSS class to element
|
|
127
|
-
* @param elementId - DOM element ID
|
|
128
|
-
* @param className - Class to toggle
|
|
129
|
-
* @param condition - Optional condition function
|
|
130
|
-
*/
|
|
131
|
-
bindClass(elementId: string, className: string, condition?: (val: T) => boolean): () => void {
|
|
132
|
-
const effect = new ClassEffect(elementId, className, condition);
|
|
133
|
-
this._effects.add(effect);
|
|
134
|
-
effect.run(this._value);
|
|
135
|
-
|
|
136
|
-
return () => this._effects.delete(effect);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Bind innerHTML to element
|
|
141
|
-
* @param elementId - DOM element ID
|
|
142
|
-
* @param transform - Optional transform function
|
|
143
|
-
*/
|
|
144
|
-
bindHTML(elementId: string, transform?: (val: T) => string): () => void {
|
|
145
|
-
const effect = new HTMLEffect(elementId, transform);
|
|
146
|
-
this._effects.add(effect);
|
|
147
|
-
effect.run(this._value);
|
|
148
|
-
|
|
149
|
-
return () => this._effects.delete(effect);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/* -------------------------
|
|
153
|
-
* Number Helpers
|
|
154
|
-
* ------------------------- */
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Increment (numbers only)
|
|
158
|
-
*/
|
|
159
|
-
increment = (): void => {
|
|
48
|
+
increment(): void {
|
|
160
49
|
if (typeof this._value === 'number') {
|
|
161
50
|
this.set((this._value + 1) as T);
|
|
162
51
|
}
|
|
163
52
|
}
|
|
164
53
|
|
|
165
|
-
|
|
166
|
-
* Decrement (numbers only)
|
|
167
|
-
*/
|
|
168
|
-
decrement = (): void => {
|
|
54
|
+
decrement(): void {
|
|
169
55
|
if (typeof this._value === 'number') {
|
|
170
56
|
this.set((this._value - 1) as T);
|
|
171
57
|
}
|
|
172
58
|
}
|
|
173
59
|
|
|
174
|
-
|
|
175
|
-
* Add amount (returns function for event handlers)
|
|
176
|
-
*/
|
|
177
|
-
add = (amount: number) => (): void => {
|
|
60
|
+
add(amount: number): void {
|
|
178
61
|
if (typeof this._value === 'number') {
|
|
179
62
|
this.set((this._value + amount) as T);
|
|
180
63
|
}
|
|
181
64
|
}
|
|
182
65
|
|
|
183
|
-
|
|
184
|
-
* Subtract amount
|
|
185
|
-
*/
|
|
186
|
-
subtract = (amount: number) => (): void => {
|
|
66
|
+
subtract(amount: number): void {
|
|
187
67
|
if (typeof this._value === 'number') {
|
|
188
68
|
this.set((this._value - amount) as T);
|
|
189
69
|
}
|
|
190
70
|
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Multiply by factor
|
|
194
|
-
*/
|
|
195
|
-
multiply = (factor: number) => (): void => {
|
|
196
|
-
if (typeof this._value === 'number') {
|
|
197
|
-
this.set((this._value * factor) as T);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/* -------------------------
|
|
202
|
-
* Boolean Helpers
|
|
203
|
-
* ------------------------- */
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Toggle (booleans only)
|
|
207
|
-
*/
|
|
208
|
-
toggle = (): void => {
|
|
209
|
-
if (typeof this._value === 'boolean') {
|
|
210
|
-
this.set((!this._value) as T);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/* -------------------------
|
|
215
|
-
* General Helpers
|
|
216
|
-
* ------------------------- */
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Reset to initial value (returns function)
|
|
220
|
-
*/
|
|
221
|
-
reset = (initialValue: T) => (): void => {
|
|
222
|
-
this.set(initialValue);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/* -------------------------
|
|
227
|
-
* Effect Classes
|
|
228
|
-
* ------------------------- */
|
|
229
|
-
|
|
230
|
-
interface Effect {
|
|
231
|
-
run(value: any): void;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
class VisibilityEffect implements Effect {
|
|
235
|
-
constructor(
|
|
236
|
-
private elementId: string
|
|
237
|
-
) { }
|
|
238
|
-
|
|
239
|
-
run(visible: boolean): void {
|
|
240
|
-
const el = document.getElementById(this.elementId);
|
|
241
|
-
if (!el) return;
|
|
242
|
-
|
|
243
|
-
// Simple display toggle (Vue v-show style)
|
|
244
|
-
el.style.display = visible ? '' : 'none';
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
interface VisibilityOptions {
|
|
249
|
-
mode?: 'display' | 'visibility' | 'opacity' | 'class';
|
|
250
|
-
visibleClass?: string;
|
|
251
|
-
hiddenClass?: string;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
class TextEffect implements Effect {
|
|
255
|
-
constructor(
|
|
256
|
-
private elementId: string,
|
|
257
|
-
private transform?: (val: any) => string
|
|
258
|
-
) { }
|
|
259
|
-
|
|
260
|
-
run(value: any): void {
|
|
261
|
-
const el = document.getElementById(this.elementId);
|
|
262
|
-
if (!el) return;
|
|
263
|
-
|
|
264
|
-
const text = this.transform ? this.transform(value) : String(value);
|
|
265
|
-
el.textContent = text;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
class HTMLEffect implements Effect {
|
|
270
|
-
constructor(
|
|
271
|
-
private elementId: string,
|
|
272
|
-
private transform?: (val: any) => string
|
|
273
|
-
) { }
|
|
274
|
-
|
|
275
|
-
run(value: any): void {
|
|
276
|
-
const el = document.getElementById(this.elementId);
|
|
277
|
-
if (!el) return;
|
|
278
|
-
|
|
279
|
-
const html = this.transform ? this.transform(value) : String(value);
|
|
280
|
-
el.innerHTML = html;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
class AttrEffect implements Effect {
|
|
285
|
-
constructor(
|
|
286
|
-
private elementId: string,
|
|
287
|
-
private attrName: string,
|
|
288
|
-
private transform?: (val: any) => string
|
|
289
|
-
) { }
|
|
290
|
-
|
|
291
|
-
run(value: any): void {
|
|
292
|
-
const el = document.getElementById(this.elementId);
|
|
293
|
-
if (!el) return;
|
|
294
|
-
|
|
295
|
-
const attrValue = this.transform ? this.transform(value) : String(value);
|
|
296
|
-
el.setAttribute(this.attrName, attrValue);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
class ClassEffect implements Effect {
|
|
301
|
-
constructor(
|
|
302
|
-
private elementId: string,
|
|
303
|
-
private className: string,
|
|
304
|
-
private condition?: (val: any) => boolean
|
|
305
|
-
) { }
|
|
306
|
-
|
|
307
|
-
run(value: any): void {
|
|
308
|
-
const el = document.getElementById(this.elementId);
|
|
309
|
-
if (!el) return;
|
|
310
|
-
|
|
311
|
-
const shouldAdd = this.condition ? this.condition(value) : Boolean(value);
|
|
312
|
-
if (shouldAdd) {
|
|
313
|
-
el.classList.add(this.className);
|
|
314
|
-
} else {
|
|
315
|
-
el.classList.remove(this.className);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
class ValueEffect implements Effect {
|
|
321
|
-
constructor(
|
|
322
|
-
private elementId: string,
|
|
323
|
-
private transform?: (val: any) => number | string
|
|
324
|
-
) { }
|
|
325
|
-
|
|
326
|
-
run(value: any): void {
|
|
327
|
-
const el = document.getElementById(this.elementId);
|
|
328
|
-
if (!el) return;
|
|
329
|
-
|
|
330
|
-
const newValue = this.transform ? this.transform(value) : value;
|
|
331
|
-
|
|
332
|
-
// For progress/jux components, set data-value and trigger update
|
|
333
|
-
if (el.classList.contains('jux-progress')) {
|
|
334
|
-
el.setAttribute('data-value', String(newValue));
|
|
335
|
-
|
|
336
|
-
// Find the progress instance and call value() to trigger update
|
|
337
|
-
const progressBar = el.querySelector('.jux-progress-bar');
|
|
338
|
-
if (progressBar) {
|
|
339
|
-
const percentage = typeof newValue === 'number' ? newValue : parseFloat(String(newValue));
|
|
340
|
-
const max = parseFloat(progressBar.getAttribute('aria-valuemax') || '100');
|
|
341
|
-
const percentageValue = (percentage / max) * 100;
|
|
342
|
-
(progressBar as HTMLElement).style.width = `${percentageValue}%`;
|
|
343
|
-
progressBar.setAttribute('aria-valuenow', String(percentage));
|
|
344
|
-
}
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// For native HTML elements (input, progress, meter)
|
|
349
|
-
if ('value' in el) {
|
|
350
|
-
(el as any).value = String(newValue);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
if (el instanceof HTMLProgressElement || el.tagName === 'METER') {
|
|
354
|
-
el.setAttribute('value', String(newValue));
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
71
|
}
|
|
358
72
|
|
|
359
73
|
/**
|
|
360
|
-
*
|
|
74
|
+
* Factory function to create reactive state
|
|
361
75
|
*/
|
|
362
76
|
export function state<T>(initialValue: T): State<T> {
|
|
363
77
|
return new State(initialValue);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "juxscript",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.20",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A JavaScript UX authorship platform",
|
|
6
6
|
"main": "lib/jux.js",
|
|
@@ -55,7 +55,6 @@
|
|
|
55
55
|
"esbuild": "^0.19.0",
|
|
56
56
|
"express": "^4.18.2",
|
|
57
57
|
"glob": "^13.0.0",
|
|
58
|
-
"sql.js": "^1.8.0",
|
|
59
58
|
"ws": "^8.13.0"
|
|
60
59
|
},
|
|
61
60
|
"devDependencies": {
|