juxscript 1.1.266 → 1.1.269

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.
@@ -1,32 +1,16 @@
1
1
  declare class PageState {
2
2
  private _registry;
3
3
  private _proxy;
4
+ static readonly WIRE_EVENTS: readonly ["blur", "focus", "click", "dblclick", "change", "input", "keydown", "keyup", "keypress", "mouseenter", "mouseleave", "submit"];
4
5
  constructor();
5
6
  private _createComponentProxy;
6
- /**
7
- * Register a component with pageState.
8
- * Called automatically by jux components on creation.
9
- */
10
7
  private _register;
11
8
  private _wireEvent;
9
+ private _findElement;
12
10
  private _unregister;
13
- /**
14
- * Notify all reactive blocks that depend on this key
15
- */
16
11
  private _notify;
17
- /**
18
- * Create a reactive block that re-runs when its dependencies change.
19
- *
20
- * Usage:
21
- * pageState.__watch(() => {
22
- * if (pageState['input1'].value === 'blueberry') {
23
- * pageState['input2'].value = 'raspberry';
24
- * }
25
- * });
26
- */
27
12
  private _watch;
28
13
  getProxy(): Record<string, any>;
29
- __notify(depKey: string): void;
30
14
  }
31
15
  export declare const pageState: Record<string, any>;
32
16
  export { PageState };
@@ -1 +1 @@
1
- {"version":3,"file":"pageState.d.ts","sourceRoot":"","sources":["../../../lib/state/pageState.ts"],"names":[],"mappings":"AAaA,cAAM,SAAS;IACX,OAAO,CAAC,SAAS,CAA0C;IAC3D,OAAO,CAAC,MAAM,CAAsB;;IA6BpC,OAAO,CAAC,qBAAqB;IAkE7B;;;OAGG;IACH,OAAO,CAAC,SAAS;IA4CjB,OAAO,CAAC,UAAU;IAkBlB,OAAO,CAAC,WAAW;IAInB;;OAEG;IACH,OAAO,CAAC,OAAO;IAQf;;;;;;;;;OASG;IACH,OAAO,CAAC,MAAM;IAgBd,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAI/B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CAOjC;AAID,eAAO,MAAM,SAAS,qBAAuB,CAAC;AAE9C,OAAO,EAAE,SAAS,EAAE,CAAC"}
1
+ {"version":3,"file":"pageState.d.ts","sourceRoot":"","sources":["../../../lib/state/pageState.ts"],"names":[],"mappings":"AAeA,cAAM,SAAS;IACX,OAAO,CAAC,SAAS,CAA0C;IAC3D,OAAO,CAAC,MAAM,CAAsB;IAEpC,MAAM,CAAC,QAAQ,CAAC,WAAW,2IAOhB;;IA2BX,OAAO,CAAC,qBAAqB;IAyD7B,OAAO,CAAC,SAAS;IA6CjB,OAAO,CAAC,UAAU;IAwBlB,OAAO,CAAC,YAAY;IAkBpB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,OAAO;IAqBf,OAAO,CAAC,MAAM;IAcd,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;CAGlC;AAID,eAAO,MAAM,SAAS,qBAAuB,CAAC;AAE9C,OAAO,EAAE,SAAS,EAAE,CAAC"}
@@ -1,6 +1,8 @@
1
1
  // Tracks which reactive block is currently running so we can record dependencies
2
2
  let activeReaction = null;
3
3
  const reactionDeps = new Map();
4
+ let notifyQueue = [];
5
+ let isNotifying = false;
4
6
  class PageState {
5
7
  constructor() {
6
8
  this._registry = new Map();
@@ -19,11 +21,9 @@ class PageState {
19
21
  const entry = this._registry.get(id);
20
22
  if (!entry)
21
23
  return undefined;
22
- // Return a proxy for the component's state slice
23
24
  return this._createComponentProxy(id, entry);
24
25
  },
25
26
  set: (_, id, value) => {
26
- // pageState['input1'] = someComponent — register it
27
27
  if (value && typeof value === 'object' && value.id) {
28
28
  this._register(value);
29
29
  return true;
@@ -36,27 +36,22 @@ class PageState {
36
36
  return new Proxy({}, {
37
37
  get: (_, prop) => {
38
38
  const depKey = `${id}.${prop}`;
39
- // Track dependency if inside a reactive block
40
39
  if (activeReaction) {
41
40
  if (!reactionDeps.has(activeReaction)) {
42
41
  reactionDeps.set(activeReaction, new Set());
43
42
  }
44
43
  reactionDeps.get(activeReaction).add(depKey);
45
44
  }
46
- // Event flags (blur, focus, hover, etc.)
47
45
  if (prop in entry.events) {
48
46
  return entry.events[prop];
49
47
  }
50
- // Component props (value, disabled, checked, etc.)
51
48
  if (prop in entry.props) {
52
49
  return entry.props[prop];
53
50
  }
54
- // ✅ Expose component methods directly (refresh, fetch, etc.)
55
51
  const comp = entry.component;
56
52
  if (typeof comp[prop] === 'function') {
57
53
  return comp[prop].bind(comp);
58
54
  }
59
- // Fallback: try getter pattern
60
55
  const getterName = `get${prop.charAt(0).toUpperCase()}${prop.slice(1)}`;
61
56
  if (typeof comp[getterName] === 'function') {
62
57
  return comp[getterName]();
@@ -68,7 +63,6 @@ class PageState {
68
63
  },
69
64
  set: (_, prop, value) => {
70
65
  const depKey = `${id}.${prop}`;
71
- // Update component via fluent API or setter
72
66
  const comp = entry.component;
73
67
  const setterName = `set${prop.charAt(0).toUpperCase()}${prop.slice(1)}`;
74
68
  if (typeof comp[setterName] === 'function') {
@@ -77,18 +71,12 @@ class PageState {
77
71
  else if (typeof comp[prop] === 'function') {
78
72
  comp[prop](value);
79
73
  }
80
- // Update tracked props
81
74
  entry.props[prop] = value;
82
- // Notify listeners
83
75
  this._notify(depKey);
84
76
  return true;
85
77
  }
86
78
  });
87
79
  }
88
- /**
89
- * Register a component with pageState.
90
- * Called automatically by jux components on creation.
91
- */
92
80
  _register(component) {
93
81
  const id = component.id;
94
82
  if (!id)
@@ -99,7 +87,6 @@ class PageState {
99
87
  events: {},
100
88
  listeners: new Map()
101
89
  };
102
- // Seed initial props from component
103
90
  if (component.getValue)
104
91
  entry.props.value = component.getValue();
105
92
  if (component.getValues)
@@ -115,7 +102,6 @@ class PageState {
115
102
  }
116
103
  entry.props.id = id;
117
104
  this._registry.set(id, entry);
118
- // Wire component onChange to update pageState
119
105
  if (typeof component.onChange === 'function') {
120
106
  const originalOnChange = component._onChange;
121
107
  component.onChange((val, e) => {
@@ -123,6 +109,10 @@ class PageState {
123
109
  entry.props.values = val;
124
110
  this._notify(`${id}.values`);
125
111
  }
112
+ else if (typeof val === 'boolean') {
113
+ entry.props.checked = val;
114
+ this._notify(`${id}.checked`);
115
+ }
126
116
  else {
127
117
  entry.props.value = val;
128
118
  this._notify(`${id}.value`);
@@ -131,51 +121,75 @@ class PageState {
131
121
  originalOnChange(val, e);
132
122
  });
133
123
  }
134
- // Wire common DOM events as boolean flags
135
- this._wireEvent(id, entry, 'blur');
136
- this._wireEvent(id, entry, 'focus');
124
+ for (const eventName of PageState.WIRE_EVENTS) {
125
+ this._wireEvent(id, entry, eventName);
126
+ }
137
127
  }
138
128
  _wireEvent(id, entry, eventName) {
139
129
  entry.events[eventName] = false;
140
- // Find the DOM element
141
- const el = entry.component._element || entry.component.getElement?.();
142
- if (!el || !(el instanceof HTMLElement))
130
+ const el = this._findElement(entry.component);
131
+ if (!el)
143
132
  return;
144
- el.addEventListener(eventName, () => {
133
+ el.addEventListener(eventName, (e) => {
145
134
  entry.events[eventName] = true;
135
+ if ((eventName === 'input' || eventName === 'change') && el instanceof HTMLInputElement) {
136
+ entry.props.value = el.value;
137
+ }
138
+ if ((eventName === 'input' || eventName === 'change') && el instanceof HTMLSelectElement) {
139
+ entry.props.value = el.value;
140
+ }
146
141
  this._notify(`${id}.${eventName}`);
147
- // Reset after reactions run
148
142
  queueMicrotask(() => {
149
143
  entry.events[eventName] = false;
150
144
  });
151
145
  });
152
146
  }
147
+ _findElement(component) {
148
+ if (component._element instanceof HTMLElement)
149
+ return component._element;
150
+ if (typeof component.getElement === 'function') {
151
+ const el = component.getElement();
152
+ if (el instanceof HTMLElement)
153
+ return el;
154
+ }
155
+ if (component._wrapper instanceof HTMLElement) {
156
+ const input = component._wrapper.querySelector('input, select, textarea, button');
157
+ if (input)
158
+ return input;
159
+ return component._wrapper;
160
+ }
161
+ if (component.id && typeof document !== 'undefined') {
162
+ const el = document.getElementById(component.id);
163
+ if (el)
164
+ return el;
165
+ }
166
+ return null;
167
+ }
153
168
  _unregister(id) {
154
169
  this._registry.delete(id);
155
170
  }
156
- /**
157
- * Notify all reactive blocks that depend on this key
158
- */
159
171
  _notify(depKey) {
160
- for (const [reaction, deps] of reactionDeps.entries()) {
161
- if (deps.has(depKey)) {
162
- reaction();
172
+ notifyQueue.push(depKey);
173
+ // Prevent re-entrant notification loops
174
+ if (isNotifying)
175
+ return;
176
+ isNotifying = true;
177
+ try {
178
+ while (notifyQueue.length > 0) {
179
+ const key = notifyQueue.shift();
180
+ for (const [reaction, deps] of reactionDeps.entries()) {
181
+ if (deps.has(key)) {
182
+ reaction();
183
+ }
184
+ }
163
185
  }
164
186
  }
187
+ finally {
188
+ isNotifying = false;
189
+ }
165
190
  }
166
- /**
167
- * Create a reactive block that re-runs when its dependencies change.
168
- *
169
- * Usage:
170
- * pageState.__watch(() => {
171
- * if (pageState['input1'].value === 'blueberry') {
172
- * pageState['input2'].value = 'raspberry';
173
- * }
174
- * });
175
- */
176
191
  _watch(fn) {
177
192
  const reaction = () => {
178
- // Clear old deps, track new ones during execution
179
193
  reactionDeps.set(reaction, new Set());
180
194
  activeReaction = reaction;
181
195
  try {
@@ -185,20 +199,20 @@ class PageState {
185
199
  activeReaction = null;
186
200
  }
187
201
  };
188
- // Run once immediately to collect initial dependencies
189
202
  reaction();
190
203
  }
191
204
  getProxy() {
192
205
  return this._proxy;
193
206
  }
194
- __notify(depKey) {
195
- for (const [reaction, deps] of reactionDeps.entries()) {
196
- if (deps.has(depKey)) {
197
- reaction();
198
- }
199
- }
200
- }
201
207
  }
208
+ PageState.WIRE_EVENTS = [
209
+ 'blur', 'focus',
210
+ 'click', 'dblclick',
211
+ 'change', 'input',
212
+ 'keydown', 'keyup', 'keypress',
213
+ 'mouseenter', 'mouseleave',
214
+ 'submit'
215
+ ];
202
216
  // Singleton
203
217
  const _instance = new PageState();
204
218
  export const pageState = _instance.getProxy();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.266",
3
+ "version": "1.1.269",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "./dist/lib/index.js",