juxscript 1.1.254 → 1.1.256
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/dist/lib/components/tag.d.ts +27 -17
- package/dist/lib/components/tag.d.ts.map +1 -1
- package/dist/lib/components/tag.js +43 -27
- package/dist/lib/index.d.ts +3 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +31 -10
- package/dist/lib/state/pageState.d.ts +47 -0
- package/dist/lib/state/pageState.d.ts.map +1 -0
- package/dist/lib/state/pageState.js +214 -0
- package/package.json +1 -1
|
@@ -1,23 +1,33 @@
|
|
|
1
|
+
interface TagOptions {
|
|
2
|
+
content?: string;
|
|
3
|
+
class?: string;
|
|
4
|
+
style?: string;
|
|
5
|
+
[key: string]: any;
|
|
6
|
+
}
|
|
1
7
|
declare class Tag {
|
|
2
8
|
id: string;
|
|
3
|
-
content: string;
|
|
4
|
-
options: Record<string, any>;
|
|
5
9
|
tagName: string;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
opts: TagOptions;
|
|
11
|
+
private _element;
|
|
12
|
+
constructor(id: string, tagName: string, options?: TagOptions);
|
|
13
|
+
content(value: string): this;
|
|
14
|
+
class(value: string): this;
|
|
15
|
+
style(value: string): this;
|
|
16
|
+
render(target?: string | HTMLElement): this;
|
|
17
|
+
getElement(): HTMLElement | null;
|
|
9
18
|
}
|
|
10
|
-
export declare function tag(
|
|
11
|
-
export declare function div(
|
|
12
|
-
export declare function span(
|
|
13
|
-
export declare function p(
|
|
14
|
-
export declare function code(
|
|
15
|
-
export declare function pre(
|
|
16
|
-
export declare function h1(
|
|
17
|
-
export declare function h2(
|
|
18
|
-
export declare function h3(
|
|
19
|
-
export declare function h4(
|
|
20
|
-
export declare function h5(
|
|
21
|
-
export declare function h6(
|
|
19
|
+
export declare function tag(id: string, tagName: string, options?: TagOptions): Tag;
|
|
20
|
+
export declare function div(id: string, options?: TagOptions): Tag;
|
|
21
|
+
export declare function span(id: string, options?: TagOptions): Tag;
|
|
22
|
+
export declare function p(id: string, options?: TagOptions): Tag;
|
|
23
|
+
export declare function code(id: string, options?: TagOptions): Tag;
|
|
24
|
+
export declare function pre(id: string, options?: TagOptions): Tag;
|
|
25
|
+
export declare function h1(id: string, options?: TagOptions): Tag;
|
|
26
|
+
export declare function h2(id: string, options?: TagOptions): Tag;
|
|
27
|
+
export declare function h3(id: string, options?: TagOptions): Tag;
|
|
28
|
+
export declare function h4(id: string, options?: TagOptions): Tag;
|
|
29
|
+
export declare function h5(id: string, options?: TagOptions): Tag;
|
|
30
|
+
export declare function h6(id: string, options?: TagOptions): Tag;
|
|
31
|
+
export { Tag, TagOptions };
|
|
22
32
|
export default tag;
|
|
23
33
|
//# sourceMappingURL=tag.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tag.d.ts","sourceRoot":"","sources":["../../../lib/components/tag.ts"],"names":[],"mappings":"AAEA,cAAM,GAAG;IACL,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"tag.d.ts","sourceRoot":"","sources":["../../../lib/components/tag.ts"],"names":[],"mappings":"AAEA,UAAU,UAAU;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACtB;AAED,cAAM,GAAG;IACL,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,CAAC,QAAQ,CAA4B;gBAEhC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe;IAOjE,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAC5B,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAC1B,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAE1B,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW;IAuBpC,UAAU,IAAI,WAAW,GAAG,IAAI;CACnC;AAED,wBAAgB,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,OAIxE;AAED,wBAAgB,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,OAAqC;AAC7F,wBAAgB,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,OAAsC;AAC/F,wBAAgB,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,OAAmC;AACzF,wBAAgB,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,OAAsC;AAC/F,wBAAgB,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,OAAqC;AAC7F,wBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,OAAoC;AAC3F,wBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,OAAoC;AAC3F,wBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,OAAoC;AAC3F,wBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,OAAoC;AAC3F,wBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,OAAoC;AAC3F,wBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,OAAoC;AAE3F,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC;AAC3B,eAAe,GAAG,CAAC"}
|
|
@@ -1,40 +1,56 @@
|
|
|
1
1
|
import generateId from '../utils/idgen.js';
|
|
2
2
|
class Tag {
|
|
3
|
-
constructor(
|
|
4
|
-
this.
|
|
5
|
-
this.
|
|
6
|
-
this.
|
|
7
|
-
this.
|
|
3
|
+
constructor(id, tagName, options = {}) {
|
|
4
|
+
this._element = null;
|
|
5
|
+
this.id = id || generateId();
|
|
6
|
+
this.tagName = tagName;
|
|
7
|
+
this.opts = options;
|
|
8
8
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
// Fluent API
|
|
10
|
+
content(value) { this.opts.content = value; if (this._element)
|
|
11
|
+
this._element.textContent = value; return this; }
|
|
12
|
+
class(value) { this.opts.class = value; return this; }
|
|
13
|
+
style(value) { this.opts.style = value; if (this._element)
|
|
14
|
+
this._element.setAttribute('style', value); return this; }
|
|
15
|
+
render(target) {
|
|
13
16
|
const el = document.createElement(this.tagName);
|
|
14
17
|
el.id = this.id;
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
if (this.opts.content)
|
|
19
|
+
el.textContent = this.opts.content;
|
|
20
|
+
if (this.opts.class)
|
|
21
|
+
el.className = this.opts.class;
|
|
22
|
+
if (this.opts.style)
|
|
23
|
+
el.setAttribute('style', this.opts.style);
|
|
24
|
+
// Apply any extra attributes
|
|
25
|
+
for (const [key, value] of Object.entries(this.opts)) {
|
|
26
|
+
if (['content', 'class', 'style'].includes(key))
|
|
27
|
+
continue;
|
|
17
28
|
el.setAttribute(key, String(value));
|
|
18
29
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
30
|
+
this._element = el;
|
|
31
|
+
const container = target
|
|
32
|
+
? (typeof target === 'string' ? document.getElementById(target) || document.querySelector(target) : target)
|
|
33
|
+
: document.getElementById('app');
|
|
34
|
+
container?.appendChild(el);
|
|
35
|
+
return this;
|
|
22
36
|
}
|
|
37
|
+
getElement() { return this._element; }
|
|
23
38
|
}
|
|
24
|
-
export function tag(
|
|
25
|
-
const t = new Tag(
|
|
39
|
+
export function tag(id, tagName, options = {}) {
|
|
40
|
+
const t = new Tag(id, tagName, options);
|
|
26
41
|
t.render();
|
|
27
42
|
return t;
|
|
28
43
|
}
|
|
29
|
-
export function div(
|
|
30
|
-
export function span(
|
|
31
|
-
export function p(
|
|
32
|
-
export function code(
|
|
33
|
-
export function pre(
|
|
34
|
-
export function h1(
|
|
35
|
-
export function h2(
|
|
36
|
-
export function h3(
|
|
37
|
-
export function h4(
|
|
38
|
-
export function h5(
|
|
39
|
-
export function h6(
|
|
44
|
+
export function div(id, options = {}) { return tag(id, 'div', options); }
|
|
45
|
+
export function span(id, options = {}) { return tag(id, 'span', options); }
|
|
46
|
+
export function p(id, options = {}) { return tag(id, 'p', options); }
|
|
47
|
+
export function code(id, options = {}) { return tag(id, 'code', options); }
|
|
48
|
+
export function pre(id, options = {}) { return tag(id, 'pre', options); }
|
|
49
|
+
export function h1(id, options = {}) { return tag(id, 'h1', options); }
|
|
50
|
+
export function h2(id, options = {}) { return tag(id, 'h2', options); }
|
|
51
|
+
export function h3(id, options = {}) { return tag(id, 'h3', options); }
|
|
52
|
+
export function h4(id, options = {}) { return tag(id, 'h4', options); }
|
|
53
|
+
export function h5(id, options = {}) { return tag(id, 'h5', options); }
|
|
54
|
+
export function h6(id, options = {}) { return tag(id, 'h6', options); }
|
|
55
|
+
export { Tag };
|
|
40
56
|
export default tag;
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { input } from "./components/input.js";
|
|
|
4
4
|
import { select } from "./components/select.js";
|
|
5
5
|
import { radio } from "./components/radio.js";
|
|
6
6
|
import { checkbox, checkboxGroup } from "./components/checkbox.js";
|
|
7
|
+
import { pageState } from "./state/pageState.js";
|
|
7
8
|
export declare const jux: {
|
|
8
9
|
tag: typeof tag;
|
|
9
10
|
div: typeof div;
|
|
@@ -22,5 +23,7 @@ export declare const jux: {
|
|
|
22
23
|
radio: typeof radio;
|
|
23
24
|
checkbox: typeof checkbox;
|
|
24
25
|
checkboxGroup: typeof checkboxGroup;
|
|
26
|
+
pageState: Record<string, any>;
|
|
25
27
|
};
|
|
28
|
+
export { pageState };
|
|
26
29
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AACrF,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AACrF,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAYjD,eAAO,MAAM,GAAG;;;;;;;;;;;;;;;;;;;CAwBf,CAAA;AAED,OAAO,EAAE,SAAS,EAAE,CAAC"}
|
package/dist/lib/index.js
CHANGED
|
@@ -4,17 +4,38 @@ import { input } from "./components/input.js";
|
|
|
4
4
|
import { select } from "./components/select.js";
|
|
5
5
|
import { radio } from "./components/radio.js";
|
|
6
6
|
import { checkbox, checkboxGroup } from "./components/checkbox.js";
|
|
7
|
+
import { pageState } from "./state/pageState.js";
|
|
8
|
+
// Wrap factories to auto-register into pageState
|
|
9
|
+
function tracked(factory) {
|
|
10
|
+
return ((...args) => {
|
|
11
|
+
const component = factory(...args);
|
|
12
|
+
const id = args[0]; // first arg is always id
|
|
13
|
+
if (id)
|
|
14
|
+
pageState.register(id, component);
|
|
15
|
+
return component;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
7
18
|
export const jux = {
|
|
19
|
+
// Tags
|
|
8
20
|
tag,
|
|
9
|
-
div,
|
|
10
|
-
h1
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
21
|
+
div: tracked(div),
|
|
22
|
+
h1: tracked(h1),
|
|
23
|
+
h2: tracked(h2),
|
|
24
|
+
h3: tracked(h3),
|
|
25
|
+
h4: tracked(h4),
|
|
26
|
+
h5: tracked(h5),
|
|
27
|
+
h6: tracked(h6),
|
|
28
|
+
p: tracked(p),
|
|
29
|
+
span: tracked(span),
|
|
30
|
+
pre: tracked(pre),
|
|
31
|
+
// Components
|
|
14
32
|
include,
|
|
15
|
-
input,
|
|
16
|
-
select,
|
|
17
|
-
radio,
|
|
18
|
-
checkbox,
|
|
19
|
-
checkboxGroup
|
|
33
|
+
input: tracked(input),
|
|
34
|
+
select: tracked(select),
|
|
35
|
+
radio: tracked(radio),
|
|
36
|
+
checkbox: tracked(checkbox),
|
|
37
|
+
checkboxGroup: tracked(checkboxGroup),
|
|
38
|
+
// State
|
|
39
|
+
pageState
|
|
20
40
|
};
|
|
41
|
+
export { pageState };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
declare class PageState {
|
|
2
|
+
private _components;
|
|
3
|
+
private _listeners;
|
|
4
|
+
private _eventListeners;
|
|
5
|
+
private _proxy;
|
|
6
|
+
constructor();
|
|
7
|
+
/**
|
|
8
|
+
* Register a component into pageState.
|
|
9
|
+
* Called automatically by jux components on creation.
|
|
10
|
+
*/
|
|
11
|
+
register(id: string, component: any): void;
|
|
12
|
+
/**
|
|
13
|
+
* Watch for property changes on any component.
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* pageState.watch('input1', 'value', (newVal, oldVal) => { ... })
|
|
17
|
+
* pageState.watch('*', 'value', (key, prop, newVal) => { ... }) // wildcard
|
|
18
|
+
*/
|
|
19
|
+
watch(keyOrAll: string, prop: string, fn: (newVal: any, oldVal: any, key?: string) => void): () => void;
|
|
20
|
+
/**
|
|
21
|
+
* Listen for ephemeral events (blur, focus, hover, etc.)
|
|
22
|
+
*
|
|
23
|
+
* Usage:
|
|
24
|
+
* pageState.on('input2', 'blur', () => { pageState['input1'] = ''; })
|
|
25
|
+
* pageState.on('input1', 'focus', () => { ... })
|
|
26
|
+
*/
|
|
27
|
+
on(id: string, event: string, fn: (detail?: any) => void): () => void;
|
|
28
|
+
/**
|
|
29
|
+
* Emit an ephemeral event (called internally by components)
|
|
30
|
+
*/
|
|
31
|
+
emit(id: string, event: string, detail?: any): void;
|
|
32
|
+
/**
|
|
33
|
+
* Get a snapshot of all component values
|
|
34
|
+
*/
|
|
35
|
+
snapshot(): Record<string, any>;
|
|
36
|
+
/**
|
|
37
|
+
* Log current state to console
|
|
38
|
+
*/
|
|
39
|
+
dump(): void;
|
|
40
|
+
getProxy(): Record<string, any>;
|
|
41
|
+
private _notify;
|
|
42
|
+
private _wireDOMEvent;
|
|
43
|
+
private _createComponentProxy;
|
|
44
|
+
}
|
|
45
|
+
export declare const pageState: Record<string, any>;
|
|
46
|
+
export { PageState };
|
|
47
|
+
//# sourceMappingURL=pageState.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pageState.d.ts","sourceRoot":"","sources":["../../../lib/state/pageState.ts"],"names":[],"mappings":"AASA,cAAM,SAAS;IACX,OAAO,CAAC,WAAW,CAA+B;IAClD,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,eAAe,CAAoE;IAC3F,OAAO,CAAC,MAAM,CAAsB;;IA4CpC;;;OAGG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,GAAG,IAAI;IAa1C;;;;;;OAMG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI;IAUvG;;;;;;OAMG;IACH,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,GAAG,KAAK,IAAI,GAAG,MAAM,IAAI;IAqBrE;;OAEG;IACH,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,GAAG,IAAI;IAOnD;;OAEG;IACH,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAY/B;;OAEG;IACH,IAAI,IAAI,IAAI;IAIZ,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAI/B,OAAO,CAAC,OAAO;IAMf,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,qBAAqB;CA+ChC;AAID,eAAO,MAAM,SAAS,qBAAuB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,CAAC"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
class PageState {
|
|
2
|
+
constructor() {
|
|
3
|
+
this._components = new Map();
|
|
4
|
+
this._listeners = new Set();
|
|
5
|
+
this._eventListeners = new Map();
|
|
6
|
+
this._proxy = new Proxy({}, {
|
|
7
|
+
get: (_target, key) => {
|
|
8
|
+
if (key === 'register')
|
|
9
|
+
return this.register.bind(this);
|
|
10
|
+
if (key === 'watch')
|
|
11
|
+
return this.watch.bind(this);
|
|
12
|
+
if (key === 'on')
|
|
13
|
+
return this.on.bind(this);
|
|
14
|
+
if (key === 'keys')
|
|
15
|
+
return () => Array.from(this._components.keys());
|
|
16
|
+
if (key === 'snapshot')
|
|
17
|
+
return () => this.snapshot();
|
|
18
|
+
if (key === 'dump')
|
|
19
|
+
return () => this.dump();
|
|
20
|
+
const component = this._components.get(key);
|
|
21
|
+
if (!component)
|
|
22
|
+
return undefined;
|
|
23
|
+
return this._createComponentProxy(key, component);
|
|
24
|
+
},
|
|
25
|
+
set: (_target, key, value) => {
|
|
26
|
+
// Allow setting entire component value shorthand
|
|
27
|
+
// pageState['input1'] = 'hello' → sets value
|
|
28
|
+
const component = this._components.get(key);
|
|
29
|
+
if (component && typeof value === 'string') {
|
|
30
|
+
const old = component.getValue?.() ?? undefined;
|
|
31
|
+
component.setValue?.(value) ?? component.value?.(value);
|
|
32
|
+
this._notify(key, 'value', value, old);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
},
|
|
37
|
+
has: (_target, key) => {
|
|
38
|
+
return this._components.has(key);
|
|
39
|
+
},
|
|
40
|
+
ownKeys: () => {
|
|
41
|
+
return Array.from(this._components.keys());
|
|
42
|
+
},
|
|
43
|
+
getOwnPropertyDescriptor: (_target, key) => {
|
|
44
|
+
if (this._components.has(key)) {
|
|
45
|
+
return { configurable: true, enumerable: true, value: this._proxy[key] };
|
|
46
|
+
}
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Register a component into pageState.
|
|
53
|
+
* Called automatically by jux components on creation.
|
|
54
|
+
*/
|
|
55
|
+
register(id, component) {
|
|
56
|
+
this._components.set(id, component);
|
|
57
|
+
// Wire up change events to auto-notify
|
|
58
|
+
if (typeof component.onChange === 'function') {
|
|
59
|
+
const originalOnChange = component._onChange;
|
|
60
|
+
component.onChange((value, event) => {
|
|
61
|
+
originalOnChange?.(value, event);
|
|
62
|
+
this._notify(id, 'value', value, undefined);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Watch for property changes on any component.
|
|
68
|
+
*
|
|
69
|
+
* Usage:
|
|
70
|
+
* pageState.watch('input1', 'value', (newVal, oldVal) => { ... })
|
|
71
|
+
* pageState.watch('*', 'value', (key, prop, newVal) => { ... }) // wildcard
|
|
72
|
+
*/
|
|
73
|
+
watch(keyOrAll, prop, fn) {
|
|
74
|
+
const listener = (key, p, value, oldValue) => {
|
|
75
|
+
if ((keyOrAll === '*' || keyOrAll === key) && (prop === '*' || prop === p)) {
|
|
76
|
+
fn(value, oldValue, key);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
this._listeners.add(listener);
|
|
80
|
+
return () => this._listeners.delete(listener);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Listen for ephemeral events (blur, focus, hover, etc.)
|
|
84
|
+
*
|
|
85
|
+
* Usage:
|
|
86
|
+
* pageState.on('input2', 'blur', () => { pageState['input1'] = ''; })
|
|
87
|
+
* pageState.on('input1', 'focus', () => { ... })
|
|
88
|
+
*/
|
|
89
|
+
on(id, event, fn) {
|
|
90
|
+
if (!this._eventListeners.has(id)) {
|
|
91
|
+
this._eventListeners.set(id, new Map());
|
|
92
|
+
}
|
|
93
|
+
const componentEvents = this._eventListeners.get(id);
|
|
94
|
+
if (!componentEvents.has(event)) {
|
|
95
|
+
componentEvents.set(event, new Set());
|
|
96
|
+
}
|
|
97
|
+
componentEvents.get(event).add(fn);
|
|
98
|
+
// Wire DOM event if component is already registered
|
|
99
|
+
const component = this._components.get(id);
|
|
100
|
+
if (component) {
|
|
101
|
+
this._wireDOMEvent(id, event, component);
|
|
102
|
+
}
|
|
103
|
+
return () => {
|
|
104
|
+
componentEvents.get(event)?.delete(fn);
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Emit an ephemeral event (called internally by components)
|
|
109
|
+
*/
|
|
110
|
+
emit(id, event, detail) {
|
|
111
|
+
const listeners = this._eventListeners.get(id)?.get(event);
|
|
112
|
+
if (listeners) {
|
|
113
|
+
listeners.forEach(fn => fn(detail));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Get a snapshot of all component values
|
|
118
|
+
*/
|
|
119
|
+
snapshot() {
|
|
120
|
+
const result = {};
|
|
121
|
+
for (const [id, component] of this._components) {
|
|
122
|
+
result[id] = {
|
|
123
|
+
value: component.getValue?.() ?? component.opts?.value ?? undefined,
|
|
124
|
+
type: component.opts?.type ?? component.tagName ?? 'unknown',
|
|
125
|
+
id
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Log current state to console
|
|
132
|
+
*/
|
|
133
|
+
dump() {
|
|
134
|
+
console.table(this.snapshot());
|
|
135
|
+
}
|
|
136
|
+
getProxy() {
|
|
137
|
+
return this._proxy;
|
|
138
|
+
}
|
|
139
|
+
_notify(key, prop, value, oldValue) {
|
|
140
|
+
for (const listener of this._listeners) {
|
|
141
|
+
listener(key, prop, value, oldValue);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
_wireDOMEvent(id, event, component) {
|
|
145
|
+
// Find the actual DOM element
|
|
146
|
+
const el = component._element ?? component._wrapper ?? document.getElementById(id);
|
|
147
|
+
if (!el)
|
|
148
|
+
return;
|
|
149
|
+
// Avoid double-wiring
|
|
150
|
+
const wiredKey = `__jux_wired_${event}`;
|
|
151
|
+
if (el[wiredKey])
|
|
152
|
+
return;
|
|
153
|
+
el[wiredKey] = true;
|
|
154
|
+
el.addEventListener(event, (e) => {
|
|
155
|
+
this.emit(id, event, e);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
_createComponentProxy(id, component) {
|
|
159
|
+
const self = this;
|
|
160
|
+
return new Proxy({}, {
|
|
161
|
+
get(_target, prop) {
|
|
162
|
+
// Special properties
|
|
163
|
+
if (prop === 'id')
|
|
164
|
+
return id;
|
|
165
|
+
if (prop === 'value')
|
|
166
|
+
return component.getValue?.() ?? component.opts?.value ?? undefined;
|
|
167
|
+
if (prop === 'type')
|
|
168
|
+
return component.opts?.type ?? component.tagName ?? 'unknown';
|
|
169
|
+
if (prop === 'disabled')
|
|
170
|
+
return component.opts?.disabled ?? false;
|
|
171
|
+
if (prop === 'focused')
|
|
172
|
+
return document.activeElement === (component._element ?? document.getElementById(id));
|
|
173
|
+
if (prop === 'element')
|
|
174
|
+
return component._element ?? document.getElementById(id);
|
|
175
|
+
if (prop === '_component')
|
|
176
|
+
return component;
|
|
177
|
+
// Check for ephemeral event state methods
|
|
178
|
+
if (prop === 'on')
|
|
179
|
+
return (event, fn) => self.on(id, event, fn);
|
|
180
|
+
if (prop === 'watch')
|
|
181
|
+
return (p, fn) => self.watch(id, p, fn);
|
|
182
|
+
// Forward to component options
|
|
183
|
+
if (component.opts && prop in component.opts)
|
|
184
|
+
return component.opts[prop];
|
|
185
|
+
// Forward to component methods
|
|
186
|
+
if (typeof component[prop] === 'function')
|
|
187
|
+
return component[prop].bind(component);
|
|
188
|
+
return undefined;
|
|
189
|
+
},
|
|
190
|
+
set(_target, prop, value) {
|
|
191
|
+
const old = component.getValue?.() ?? component.opts?.[prop] ?? undefined;
|
|
192
|
+
if (prop === 'value') {
|
|
193
|
+
component.setValue?.(value) ?? component.value?.(value);
|
|
194
|
+
self._notify(id, 'value', value, old);
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
if (prop === 'disabled') {
|
|
198
|
+
component.disabled?.(value);
|
|
199
|
+
self._notify(id, 'disabled', value, old);
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
if (component.opts) {
|
|
203
|
+
component.opts[prop] = value;
|
|
204
|
+
self._notify(id, prop, value, old);
|
|
205
|
+
}
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// Singleton
|
|
212
|
+
const _instance = new PageState();
|
|
213
|
+
export const pageState = _instance.getProxy();
|
|
214
|
+
export { PageState };
|