juxscript 1.1.255 → 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.
@@ -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
@@ -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;AAEnE,eAAO,MAAM,GAAG;;;;;;;;;;;;;;;;;;CAaf,CAAA"}
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, h2, h3, h4, h5, h6,
11
- p,
12
- span,
13
- pre,
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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.255",
3
+ "version": "1.1.256",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "./dist/lib/index.js",