juxscript 1.1.25 → 1.1.27
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/index.d.ts +2 -1
- package/index.d.ts.map +1 -1
- package/index.js +3 -1
- package/lib/components/base/BaseComponent.d.ts +4 -4
- package/lib/components/base/BaseComponent.d.ts.map +1 -1
- package/lib/components/base/BaseComponent.js +30 -18
- package/lib/components/base/BaseComponent.ts +32 -19
- package/lib/components/registry.d.ts +60 -0
- package/lib/components/registry.d.ts.map +1 -0
- package/lib/components/registry.js +146 -0
- package/lib/components/registry.ts +164 -0
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -42,7 +42,8 @@ import { view } from './lib/components/view.js';
|
|
|
42
42
|
import { write } from './lib/components/write.js';
|
|
43
43
|
import { renderIcon, renderEmoji } from './lib/components/icons.js';
|
|
44
44
|
import { state } from './lib/reactivity/state.js';
|
|
45
|
-
|
|
45
|
+
import { registry } from './lib/components/registry.js';
|
|
46
|
+
export { state, registry };
|
|
46
47
|
export declare const jux: {
|
|
47
48
|
alert: typeof alert;
|
|
48
49
|
app: typeof app;
|
package/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAClD,OAAO,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAEhD,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AACtD,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAClD,OAAO,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAElD,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AACtD,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAElD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
|
package/index.js
CHANGED
|
@@ -44,7 +44,9 @@ import { write } from './lib/components/write.js';
|
|
|
44
44
|
import { fetchAPI } from './lib/utils/fetch.js';
|
|
45
45
|
import { renderIcon, renderEmoji } from './lib/components/icons.js';
|
|
46
46
|
import { state } from './lib/reactivity/state.js';
|
|
47
|
-
|
|
47
|
+
import { registry } from './lib/components/registry.js'; // ✅ Import registry
|
|
48
|
+
export { state, registry }; // ✅ Export registry
|
|
49
|
+
// Utilities
|
|
48
50
|
export const jux = {
|
|
49
51
|
alert,
|
|
50
52
|
app,
|
|
@@ -37,6 +37,7 @@ export declare abstract class BaseComponent<TState extends BaseState = BaseState
|
|
|
37
37
|
}>;
|
|
38
38
|
protected _triggerHandlers: Map<string, Function>;
|
|
39
39
|
protected _callbackHandlers: Map<string, Function>;
|
|
40
|
+
protected _isUpdatingSync: boolean;
|
|
40
41
|
constructor(id: string, initialState: TState);
|
|
41
42
|
protected abstract getTriggerEvents(): readonly string[];
|
|
42
43
|
protected abstract getCallbackEvents(): readonly string[];
|
|
@@ -49,8 +50,8 @@ export declare abstract class BaseComponent<TState extends BaseState = BaseState
|
|
|
49
50
|
*/
|
|
50
51
|
update(prop: string, value: any): void;
|
|
51
52
|
/**
|
|
52
|
-
* ✨
|
|
53
|
-
*
|
|
53
|
+
* ✨ Notify external State<T> objects when component state changes
|
|
54
|
+
* Prevents infinite loops with guard flag
|
|
54
55
|
*/
|
|
55
56
|
protected _notifySyncedState(prop: string, value: any): void;
|
|
56
57
|
/**
|
|
@@ -144,8 +145,7 @@ export declare abstract class BaseComponent<TState extends BaseState = BaseState
|
|
|
144
145
|
protected _setupContainer(targetId?: string): HTMLElement;
|
|
145
146
|
protected _wireStandardEvents(element: HTMLElement): void;
|
|
146
147
|
/**
|
|
147
|
-
*
|
|
148
|
-
* if it exists on the component
|
|
148
|
+
* Wire sync subscriptions with guard flag
|
|
149
149
|
*/
|
|
150
150
|
protected _wireAllSyncs(): void;
|
|
151
151
|
renderTo(juxComponent: any): this;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseComponent.d.ts","sourceRoot":"","sources":["BaseComponent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"BaseComponent.d.ts","sourceRoot":"","sources":["BaseComponent.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getOrCreateContainer } from '../helpers.js';
|
|
2
|
+
import { registry } from '../registry.js'; // ✅ Import registry
|
|
2
3
|
/**
|
|
3
4
|
* Abstract base class for all JUX components
|
|
4
5
|
* Provides common storage, event routing, and lifecycle methods
|
|
@@ -16,6 +17,7 @@ export class BaseComponent {
|
|
|
16
17
|
this._syncBindings = [];
|
|
17
18
|
this._triggerHandlers = new Map();
|
|
18
19
|
this._callbackHandlers = new Map();
|
|
20
|
+
this._isUpdatingSync = false; // ✅ Guard flag
|
|
19
21
|
this._id = id;
|
|
20
22
|
this.id = id;
|
|
21
23
|
const stateWithDefaults = {
|
|
@@ -27,7 +29,6 @@ export class BaseComponent {
|
|
|
27
29
|
attributes: {},
|
|
28
30
|
...initialState
|
|
29
31
|
};
|
|
30
|
-
// ✨ REACTIVE PROXY: Intercept state changes to trigger view updates automatically
|
|
31
32
|
this.state = new Proxy(stateWithDefaults, {
|
|
32
33
|
set: (target, prop, value) => {
|
|
33
34
|
const key = prop;
|
|
@@ -35,8 +36,10 @@ export class BaseComponent {
|
|
|
35
36
|
target[key] = value;
|
|
36
37
|
// 1️⃣ Update DOM via update()
|
|
37
38
|
this.update(prop, value);
|
|
38
|
-
// 2️⃣ ✨
|
|
39
|
-
this.
|
|
39
|
+
// 2️⃣ ✨ Notify synced State<T> objects (with guard)
|
|
40
|
+
if (!this._isUpdatingSync) {
|
|
41
|
+
this._notifySyncedState(prop, value);
|
|
42
|
+
}
|
|
40
43
|
}
|
|
41
44
|
return true;
|
|
42
45
|
}
|
|
@@ -81,18 +84,23 @@ export class BaseComponent {
|
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
86
|
/**
|
|
84
|
-
* ✨
|
|
85
|
-
*
|
|
87
|
+
* ✨ Notify external State<T> objects when component state changes
|
|
88
|
+
* Prevents infinite loops with guard flag
|
|
86
89
|
*/
|
|
87
90
|
_notifySyncedState(prop, value) {
|
|
88
91
|
const syncBinding = this._syncBindings.find(b => b.property === prop);
|
|
89
92
|
if (syncBinding) {
|
|
90
93
|
const { stateObj, toState } = syncBinding;
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
// Set guard flag to prevent recursion
|
|
95
|
+
this._isUpdatingSync = true;
|
|
96
|
+
try {
|
|
97
|
+
const transformedValue = toState ? toState(value) : value;
|
|
98
|
+
stateObj.set(transformedValue);
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
// Always clear guard flag
|
|
102
|
+
this._isUpdatingSync = false;
|
|
103
|
+
}
|
|
96
104
|
}
|
|
97
105
|
}
|
|
98
106
|
/* ═════════════════════════════════════════════════════════════════
|
|
@@ -270,6 +278,8 @@ export class BaseComponent {
|
|
|
270
278
|
if (this.container) {
|
|
271
279
|
this.container.remove();
|
|
272
280
|
this.container = null;
|
|
281
|
+
// ✅ Unregister when removed
|
|
282
|
+
registry.unregister(this._id);
|
|
273
283
|
}
|
|
274
284
|
return this;
|
|
275
285
|
}
|
|
@@ -331,14 +341,12 @@ export class BaseComponent {
|
|
|
331
341
|
_setupContainer(targetId) {
|
|
332
342
|
let container;
|
|
333
343
|
if (targetId) {
|
|
334
|
-
// Strip leading # if present
|
|
335
344
|
const id = targetId.startsWith('#') ? targetId.slice(1) : targetId;
|
|
336
345
|
const target = document.getElementById(id);
|
|
337
346
|
if (target) {
|
|
338
347
|
container = target;
|
|
339
348
|
}
|
|
340
349
|
else {
|
|
341
|
-
// Gracefully create the container instead of throwing
|
|
342
350
|
console.warn(`[Jux] Target "${targetId}" not found, creating it with graceful fallback`);
|
|
343
351
|
container = getOrCreateContainer(id);
|
|
344
352
|
}
|
|
@@ -346,9 +354,9 @@ export class BaseComponent {
|
|
|
346
354
|
else {
|
|
347
355
|
container = getOrCreateContainer(this._id);
|
|
348
356
|
}
|
|
349
|
-
// Add universal component class for DOM inspection
|
|
350
|
-
// container.classList.add('jux-component');
|
|
351
357
|
this.container = container;
|
|
358
|
+
// ✅ Auto-register component when container is set up
|
|
359
|
+
registry.register(this);
|
|
352
360
|
return container;
|
|
353
361
|
}
|
|
354
362
|
_wireStandardEvents(element) {
|
|
@@ -357,22 +365,26 @@ export class BaseComponent {
|
|
|
357
365
|
});
|
|
358
366
|
}
|
|
359
367
|
/**
|
|
360
|
-
*
|
|
361
|
-
* if it exists on the component
|
|
368
|
+
* Wire sync subscriptions with guard flag
|
|
362
369
|
*/
|
|
363
370
|
_wireAllSyncs() {
|
|
364
371
|
this._syncBindings.forEach(({ property, stateObj, toComponent }) => {
|
|
365
372
|
const transform = toComponent || ((v) => v);
|
|
366
|
-
// Check if component has a method matching the property name
|
|
367
373
|
const method = this[property];
|
|
368
374
|
if (typeof method === 'function') {
|
|
369
375
|
// Set initial value
|
|
370
376
|
const initialValue = transform(stateObj.value);
|
|
377
|
+
this._isUpdatingSync = true; // ✅ Guard during initial set
|
|
371
378
|
method.call(this, initialValue);
|
|
372
|
-
|
|
379
|
+
this._isUpdatingSync = false;
|
|
380
|
+
// Subscribe to changes with guard
|
|
373
381
|
stateObj.subscribe((val) => {
|
|
382
|
+
if (this._isUpdatingSync)
|
|
383
|
+
return; // ✅ Skip if updating
|
|
374
384
|
const transformed = transform(val);
|
|
385
|
+
this._isUpdatingSync = true;
|
|
375
386
|
method.call(this, transformed);
|
|
387
|
+
this._isUpdatingSync = false;
|
|
376
388
|
});
|
|
377
389
|
}
|
|
378
390
|
else {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { State } from '../../reactivity/state.js';
|
|
2
2
|
import { getOrCreateContainer } from '../helpers.js';
|
|
3
|
+
import { registry } from '../registry.js'; // ✅ Import registry
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Base state interface that ALL component states must extend
|
|
@@ -40,6 +41,7 @@ export abstract class BaseComponent<TState extends BaseState = BaseState> {
|
|
|
40
41
|
}> = [];
|
|
41
42
|
protected _triggerHandlers: Map<string, Function> = new Map();
|
|
42
43
|
protected _callbackHandlers: Map<string, Function> = new Map();
|
|
44
|
+
protected _isUpdatingSync: boolean = false; // ✅ Guard flag
|
|
43
45
|
|
|
44
46
|
constructor(id: string, initialState: TState) {
|
|
45
47
|
this._id = id;
|
|
@@ -55,7 +57,6 @@ export abstract class BaseComponent<TState extends BaseState = BaseState> {
|
|
|
55
57
|
...initialState
|
|
56
58
|
};
|
|
57
59
|
|
|
58
|
-
// ✨ REACTIVE PROXY: Intercept state changes to trigger view updates automatically
|
|
59
60
|
this.state = new Proxy(stateWithDefaults, {
|
|
60
61
|
set: (target, prop, value) => {
|
|
61
62
|
const key = prop as keyof TState;
|
|
@@ -65,8 +66,10 @@ export abstract class BaseComponent<TState extends BaseState = BaseState> {
|
|
|
65
66
|
// 1️⃣ Update DOM via update()
|
|
66
67
|
this.update(prop as string, value);
|
|
67
68
|
|
|
68
|
-
// 2️⃣ ✨
|
|
69
|
-
this.
|
|
69
|
+
// 2️⃣ ✨ Notify synced State<T> objects (with guard)
|
|
70
|
+
if (!this._isUpdatingSync) {
|
|
71
|
+
this._notifySyncedState(prop as string, value);
|
|
72
|
+
}
|
|
70
73
|
}
|
|
71
74
|
return true;
|
|
72
75
|
}
|
|
@@ -125,8 +128,8 @@ export abstract class BaseComponent<TState extends BaseState = BaseState> {
|
|
|
125
128
|
}
|
|
126
129
|
|
|
127
130
|
/**
|
|
128
|
-
* ✨
|
|
129
|
-
*
|
|
131
|
+
* ✨ Notify external State<T> objects when component state changes
|
|
132
|
+
* Prevents infinite loops with guard flag
|
|
130
133
|
*/
|
|
131
134
|
protected _notifySyncedState(prop: string, value: any): void {
|
|
132
135
|
const syncBinding = this._syncBindings.find(b => b.property === prop);
|
|
@@ -134,12 +137,16 @@ export abstract class BaseComponent<TState extends BaseState = BaseState> {
|
|
|
134
137
|
if (syncBinding) {
|
|
135
138
|
const { stateObj, toState } = syncBinding;
|
|
136
139
|
|
|
137
|
-
//
|
|
138
|
-
|
|
140
|
+
// Set guard flag to prevent recursion
|
|
141
|
+
this._isUpdatingSync = true;
|
|
139
142
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
+
try {
|
|
144
|
+
const transformedValue = toState ? toState(value) : value;
|
|
145
|
+
stateObj.set(transformedValue);
|
|
146
|
+
} finally {
|
|
147
|
+
// Always clear guard flag
|
|
148
|
+
this._isUpdatingSync = false;
|
|
149
|
+
}
|
|
143
150
|
}
|
|
144
151
|
}
|
|
145
152
|
|
|
@@ -340,6 +347,9 @@ export abstract class BaseComponent<TState extends BaseState = BaseState> {
|
|
|
340
347
|
if (this.container) {
|
|
341
348
|
this.container.remove();
|
|
342
349
|
this.container = null;
|
|
350
|
+
|
|
351
|
+
// ✅ Unregister when removed
|
|
352
|
+
registry.unregister(this._id);
|
|
343
353
|
}
|
|
344
354
|
return this;
|
|
345
355
|
}
|
|
@@ -409,13 +419,11 @@ export abstract class BaseComponent<TState extends BaseState = BaseState> {
|
|
|
409
419
|
protected _setupContainer(targetId?: string): HTMLElement {
|
|
410
420
|
let container: HTMLElement;
|
|
411
421
|
if (targetId) {
|
|
412
|
-
// Strip leading # if present
|
|
413
422
|
const id = targetId.startsWith('#') ? targetId.slice(1) : targetId;
|
|
414
423
|
const target = document.getElementById(id);
|
|
415
424
|
if (target) {
|
|
416
425
|
container = target;
|
|
417
426
|
} else {
|
|
418
|
-
// Gracefully create the container instead of throwing
|
|
419
427
|
console.warn(`[Jux] Target "${targetId}" not found, creating it with graceful fallback`);
|
|
420
428
|
container = getOrCreateContainer(id);
|
|
421
429
|
}
|
|
@@ -423,10 +431,11 @@ export abstract class BaseComponent<TState extends BaseState = BaseState> {
|
|
|
423
431
|
container = getOrCreateContainer(this._id);
|
|
424
432
|
}
|
|
425
433
|
|
|
426
|
-
// Add universal component class for DOM inspection
|
|
427
|
-
// container.classList.add('jux-component');
|
|
428
|
-
|
|
429
434
|
this.container = container;
|
|
435
|
+
|
|
436
|
+
// ✅ Auto-register component when container is set up
|
|
437
|
+
registry.register(this);
|
|
438
|
+
|
|
430
439
|
return container;
|
|
431
440
|
}
|
|
432
441
|
|
|
@@ -437,25 +446,29 @@ export abstract class BaseComponent<TState extends BaseState = BaseState> {
|
|
|
437
446
|
}
|
|
438
447
|
|
|
439
448
|
/**
|
|
440
|
-
*
|
|
441
|
-
* if it exists on the component
|
|
449
|
+
* Wire sync subscriptions with guard flag
|
|
442
450
|
*/
|
|
443
451
|
protected _wireAllSyncs(): void {
|
|
444
452
|
this._syncBindings.forEach(({ property, stateObj, toComponent }) => {
|
|
445
453
|
const transform = toComponent || ((v: any) => v);
|
|
446
454
|
|
|
447
|
-
// Check if component has a method matching the property name
|
|
448
455
|
const method = (this as any)[property];
|
|
449
456
|
|
|
450
457
|
if (typeof method === 'function') {
|
|
451
458
|
// Set initial value
|
|
452
459
|
const initialValue = transform(stateObj.value);
|
|
460
|
+
this._isUpdatingSync = true; // ✅ Guard during initial set
|
|
453
461
|
method.call(this, initialValue);
|
|
462
|
+
this._isUpdatingSync = false;
|
|
454
463
|
|
|
455
|
-
// Subscribe to changes
|
|
464
|
+
// Subscribe to changes with guard
|
|
456
465
|
stateObj.subscribe((val: any) => {
|
|
466
|
+
if (this._isUpdatingSync) return; // ✅ Skip if updating
|
|
467
|
+
|
|
457
468
|
const transformed = transform(val);
|
|
469
|
+
this._isUpdatingSync = true;
|
|
458
470
|
method.call(this, transformed);
|
|
471
|
+
this._isUpdatingSync = false;
|
|
459
472
|
});
|
|
460
473
|
} else {
|
|
461
474
|
console.warn(
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global component registry for debugging and console access
|
|
3
|
+
* All rendered components are automatically registered here
|
|
4
|
+
*
|
|
5
|
+
* Usage in console:
|
|
6
|
+
* __jux.get('hero-1').state.title = 'New Title'
|
|
7
|
+
* __jux.list()
|
|
8
|
+
* __jux.find('hero')
|
|
9
|
+
* __jux.create('button', 'btn-1', { label: 'Click Me' }).render()
|
|
10
|
+
*/
|
|
11
|
+
import { BaseComponent } from './base/BaseComponent.js';
|
|
12
|
+
declare class ComponentRegistry {
|
|
13
|
+
private components;
|
|
14
|
+
/**
|
|
15
|
+
* Register a component instance
|
|
16
|
+
*/
|
|
17
|
+
register(component: BaseComponent<any>): void;
|
|
18
|
+
/**
|
|
19
|
+
* Unregister a component
|
|
20
|
+
*/
|
|
21
|
+
unregister(id: string): void;
|
|
22
|
+
/**
|
|
23
|
+
* Get a component by ID
|
|
24
|
+
*/
|
|
25
|
+
get<T extends BaseComponent<any>>(id: string): T | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Check if component exists
|
|
28
|
+
*/
|
|
29
|
+
has(id: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* List all registered component IDs
|
|
32
|
+
*/
|
|
33
|
+
list(): string[];
|
|
34
|
+
/**
|
|
35
|
+
* Find components by partial ID match
|
|
36
|
+
*/
|
|
37
|
+
find(pattern: string): BaseComponent<any>[];
|
|
38
|
+
/**
|
|
39
|
+
* Get all components
|
|
40
|
+
*/
|
|
41
|
+
all(): BaseComponent<any>[];
|
|
42
|
+
/**
|
|
43
|
+
* Get components by type (constructor name)
|
|
44
|
+
*/
|
|
45
|
+
byType(typeName: string): BaseComponent<any>[];
|
|
46
|
+
/**
|
|
47
|
+
* Clear all registered components
|
|
48
|
+
*/
|
|
49
|
+
clear(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Get registry stats
|
|
52
|
+
*/
|
|
53
|
+
stats(): {
|
|
54
|
+
total: number;
|
|
55
|
+
types: Record<string, number>;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export declare const registry: ComponentRegistry;
|
|
59
|
+
export {};
|
|
60
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD,cAAM,iBAAiB;IACnB,OAAO,CAAC,UAAU,CAA8C;IAEhE;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI;IAK7C;;OAEG;IACH,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAK5B;;OAEG;IACH,GAAG,CAAC,CAAC,SAAS,aAAa,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAI5D;;OAEG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIxB;;OAEG;IACH,IAAI,IAAI,MAAM,EAAE;IAIhB;;OAEG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,EAAE;IAM3C;;OAEG;IACH,GAAG,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE;IAI3B;;OAEG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,EAAE;IAK9C;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;OAEG;IACH,KAAK,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE;CAa5D;AAGD,eAAO,MAAM,QAAQ,mBAA0B,CAAC"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global component registry for debugging and console access
|
|
3
|
+
* All rendered components are automatically registered here
|
|
4
|
+
*
|
|
5
|
+
* Usage in console:
|
|
6
|
+
* __jux.get('hero-1').state.title = 'New Title'
|
|
7
|
+
* __jux.list()
|
|
8
|
+
* __jux.find('hero')
|
|
9
|
+
* __jux.create('button', 'btn-1', { label: 'Click Me' }).render()
|
|
10
|
+
*/
|
|
11
|
+
class ComponentRegistry {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.components = new Map();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Register a component instance
|
|
17
|
+
*/
|
|
18
|
+
register(component) {
|
|
19
|
+
this.components.set(component._id, component);
|
|
20
|
+
console.debug(`[Jux Registry] Registered: ${component._id}`);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Unregister a component
|
|
24
|
+
*/
|
|
25
|
+
unregister(id) {
|
|
26
|
+
this.components.delete(id);
|
|
27
|
+
console.debug(`[Jux Registry] Unregistered: ${id}`);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Get a component by ID
|
|
31
|
+
*/
|
|
32
|
+
get(id) {
|
|
33
|
+
return this.components.get(id);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Check if component exists
|
|
37
|
+
*/
|
|
38
|
+
has(id) {
|
|
39
|
+
return this.components.has(id);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* List all registered component IDs
|
|
43
|
+
*/
|
|
44
|
+
list() {
|
|
45
|
+
return Array.from(this.components.keys());
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Find components by partial ID match
|
|
49
|
+
*/
|
|
50
|
+
find(pattern) {
|
|
51
|
+
const regex = new RegExp(pattern, 'i');
|
|
52
|
+
return Array.from(this.components.values())
|
|
53
|
+
.filter(comp => regex.test(comp._id));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get all components
|
|
57
|
+
*/
|
|
58
|
+
all() {
|
|
59
|
+
return Array.from(this.components.values());
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get components by type (constructor name)
|
|
63
|
+
*/
|
|
64
|
+
byType(typeName) {
|
|
65
|
+
return Array.from(this.components.values())
|
|
66
|
+
.filter(comp => comp.constructor.name === typeName);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Clear all registered components
|
|
70
|
+
*/
|
|
71
|
+
clear() {
|
|
72
|
+
this.components.clear();
|
|
73
|
+
console.debug('[Jux Registry] Cleared all components');
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get registry stats
|
|
77
|
+
*/
|
|
78
|
+
stats() {
|
|
79
|
+
const types = {};
|
|
80
|
+
this.components.forEach(comp => {
|
|
81
|
+
const type = comp.constructor.name;
|
|
82
|
+
types[type] = (types[type] || 0) + 1;
|
|
83
|
+
});
|
|
84
|
+
return {
|
|
85
|
+
total: this.components.size,
|
|
86
|
+
types
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Global singleton instance
|
|
91
|
+
export const registry = new ComponentRegistry();
|
|
92
|
+
// Expose to window for console access
|
|
93
|
+
if (typeof window !== 'undefined') {
|
|
94
|
+
window.__jux = {
|
|
95
|
+
get: (id) => registry.get(id),
|
|
96
|
+
list: () => registry.list(),
|
|
97
|
+
find: (pattern) => registry.find(pattern),
|
|
98
|
+
all: () => registry.all(),
|
|
99
|
+
byType: (type) => registry.byType(type),
|
|
100
|
+
stats: () => registry.stats(),
|
|
101
|
+
clear: () => registry.clear(),
|
|
102
|
+
// Factory helpers for creating components from console
|
|
103
|
+
create: (type, id, options) => {
|
|
104
|
+
const jux = window.jux;
|
|
105
|
+
if (!jux || !jux[type]) {
|
|
106
|
+
console.error(`Component type "${type}" not found`);
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
return jux[type](id, options);
|
|
110
|
+
},
|
|
111
|
+
// Debug helper
|
|
112
|
+
debug: (id) => {
|
|
113
|
+
const comp = registry.get(id);
|
|
114
|
+
if (!comp) {
|
|
115
|
+
console.error(`Component "${id}" not found`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
console.group(`🔍 Component: ${id}`);
|
|
119
|
+
console.log('Type:', comp.constructor.name);
|
|
120
|
+
console.log('State:', comp.state);
|
|
121
|
+
console.log('Props:', comp.props);
|
|
122
|
+
console.log('Container:', comp.container);
|
|
123
|
+
console.log('Instance:', comp);
|
|
124
|
+
console.groupEnd();
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
console.log(`
|
|
128
|
+
🎨 Jux DevTools Available!
|
|
129
|
+
|
|
130
|
+
Available commands:
|
|
131
|
+
__jux.get('id') Get component instance
|
|
132
|
+
__jux.list() List all component IDs
|
|
133
|
+
__jux.find('pattern') Search components by ID
|
|
134
|
+
__jux.all() Get all components
|
|
135
|
+
__jux.byType('Hero') Get components by type
|
|
136
|
+
__jux.stats() Show registry statistics
|
|
137
|
+
__jux.debug('id') Debug component details
|
|
138
|
+
__jux.create('hero', 'h1', {...}) Create component from console
|
|
139
|
+
|
|
140
|
+
Example:
|
|
141
|
+
const h = __jux.get('hero-1')
|
|
142
|
+
h.state.title = 'Changed from console!'
|
|
143
|
+
h.hide()
|
|
144
|
+
h.show()
|
|
145
|
+
`);
|
|
146
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global component registry for debugging and console access
|
|
3
|
+
* All rendered components are automatically registered here
|
|
4
|
+
*
|
|
5
|
+
* Usage in console:
|
|
6
|
+
* __jux.get('hero-1').state.title = 'New Title'
|
|
7
|
+
* __jux.list()
|
|
8
|
+
* __jux.find('hero')
|
|
9
|
+
* __jux.create('button', 'btn-1', { label: 'Click Me' }).render()
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { BaseComponent } from './base/BaseComponent.js';
|
|
13
|
+
|
|
14
|
+
class ComponentRegistry {
|
|
15
|
+
private components: Map<string, BaseComponent<any>> = new Map();
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Register a component instance
|
|
19
|
+
*/
|
|
20
|
+
register(component: BaseComponent<any>): void {
|
|
21
|
+
this.components.set(component._id, component);
|
|
22
|
+
console.debug(`[Jux Registry] Registered: ${component._id}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Unregister a component
|
|
27
|
+
*/
|
|
28
|
+
unregister(id: string): void {
|
|
29
|
+
this.components.delete(id);
|
|
30
|
+
console.debug(`[Jux Registry] Unregistered: ${id}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get a component by ID
|
|
35
|
+
*/
|
|
36
|
+
get<T extends BaseComponent<any>>(id: string): T | undefined {
|
|
37
|
+
return this.components.get(id) as T;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Check if component exists
|
|
42
|
+
*/
|
|
43
|
+
has(id: string): boolean {
|
|
44
|
+
return this.components.has(id);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* List all registered component IDs
|
|
49
|
+
*/
|
|
50
|
+
list(): string[] {
|
|
51
|
+
return Array.from(this.components.keys());
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Find components by partial ID match
|
|
56
|
+
*/
|
|
57
|
+
find(pattern: string): BaseComponent<any>[] {
|
|
58
|
+
const regex = new RegExp(pattern, 'i');
|
|
59
|
+
return Array.from(this.components.values())
|
|
60
|
+
.filter(comp => regex.test(comp._id));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get all components
|
|
65
|
+
*/
|
|
66
|
+
all(): BaseComponent<any>[] {
|
|
67
|
+
return Array.from(this.components.values());
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get components by type (constructor name)
|
|
72
|
+
*/
|
|
73
|
+
byType(typeName: string): BaseComponent<any>[] {
|
|
74
|
+
return Array.from(this.components.values())
|
|
75
|
+
.filter(comp => comp.constructor.name === typeName);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Clear all registered components
|
|
80
|
+
*/
|
|
81
|
+
clear(): void {
|
|
82
|
+
this.components.clear();
|
|
83
|
+
console.debug('[Jux Registry] Cleared all components');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get registry stats
|
|
88
|
+
*/
|
|
89
|
+
stats(): { total: number; types: Record<string, number> } {
|
|
90
|
+
const types: Record<string, number> = {};
|
|
91
|
+
|
|
92
|
+
this.components.forEach(comp => {
|
|
93
|
+
const type = comp.constructor.name;
|
|
94
|
+
types[type] = (types[type] || 0) + 1;
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
total: this.components.size,
|
|
99
|
+
types
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Global singleton instance
|
|
105
|
+
export const registry = new ComponentRegistry();
|
|
106
|
+
|
|
107
|
+
// Expose to window for console access
|
|
108
|
+
if (typeof window !== 'undefined') {
|
|
109
|
+
(window as any).__jux = {
|
|
110
|
+
get: (id: string) => registry.get(id),
|
|
111
|
+
list: () => registry.list(),
|
|
112
|
+
find: (pattern: string) => registry.find(pattern),
|
|
113
|
+
all: () => registry.all(),
|
|
114
|
+
byType: (type: string) => registry.byType(type),
|
|
115
|
+
stats: () => registry.stats(),
|
|
116
|
+
clear: () => registry.clear(),
|
|
117
|
+
|
|
118
|
+
// Factory helpers for creating components from console
|
|
119
|
+
create: (type: string, id: string, options?: any) => {
|
|
120
|
+
const jux = (window as any).jux;
|
|
121
|
+
if (!jux || !jux[type]) {
|
|
122
|
+
console.error(`Component type "${type}" not found`);
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
return jux[type](id, options);
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
// Debug helper
|
|
129
|
+
debug: (id: string) => {
|
|
130
|
+
const comp = registry.get(id);
|
|
131
|
+
if (!comp) {
|
|
132
|
+
console.error(`Component "${id}" not found`);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
console.group(`🔍 Component: ${id}`);
|
|
136
|
+
console.log('Type:', comp.constructor.name);
|
|
137
|
+
console.log('State:', comp.state);
|
|
138
|
+
console.log('Props:', comp.props);
|
|
139
|
+
console.log('Container:', comp.container);
|
|
140
|
+
console.log('Instance:', comp);
|
|
141
|
+
console.groupEnd();
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
console.log(`
|
|
146
|
+
🎨 Jux DevTools Available!
|
|
147
|
+
|
|
148
|
+
Available commands:
|
|
149
|
+
__jux.get('id') Get component instance
|
|
150
|
+
__jux.list() List all component IDs
|
|
151
|
+
__jux.find('pattern') Search components by ID
|
|
152
|
+
__jux.all() Get all components
|
|
153
|
+
__jux.byType('Hero') Get components by type
|
|
154
|
+
__jux.stats() Show registry statistics
|
|
155
|
+
__jux.debug('id') Debug component details
|
|
156
|
+
__jux.create('hero', 'h1', {...}) Create component from console
|
|
157
|
+
|
|
158
|
+
Example:
|
|
159
|
+
const h = __jux.get('hero-1')
|
|
160
|
+
h.state.title = 'Changed from console!'
|
|
161
|
+
h.hide()
|
|
162
|
+
h.show()
|
|
163
|
+
`);
|
|
164
|
+
}
|