@jay-framework/runtime 0.10.0 → 0.11.0

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/index.d.ts CHANGED
@@ -44,6 +44,8 @@ interface JayEvent<EventType, ViewState> {
44
44
  coordinate: Coordinate;
45
45
  }
46
46
  type JayEventHandler<EventType, ViewState, Returns> = (event: JayEvent<EventType, ViewState>) => Returns;
47
+ /** Event type for ViewState change notifications */
48
+ declare const VIEW_STATE_CHANGE_EVENT = "viewStateChange";
47
49
  interface JayComponent<Props, ViewState, jayElement extends BaseJayElement<ViewState>> {
48
50
  element: jayElement;
49
51
  update: updateFunc<Props>;
@@ -51,6 +53,8 @@ interface JayComponent<Props, ViewState, jayElement extends BaseJayElement<ViewS
51
53
  unmount: MountFunc;
52
54
  addEventListener: (type: string, handler: JayEventHandler<any, ViewState, void>) => void;
53
55
  removeEventListener: (type: string, handler: JayEventHandler<any, ViewState, void>) => void;
56
+ /** Current ViewState (read-only, available when using @jay-framework/component) */
57
+ readonly viewState?: ViewState;
54
58
  }
55
59
  type PropsFrom<Type> = Type extends JayComponent<infer Props, any, any> ? Props : null;
56
60
  type ViewStateFrom<Type> = Type extends JayComponent<any, infer ViewState, any> ? ViewState : null;
@@ -467,6 +471,25 @@ declare function isForEach<ViewState, Item>(c: Conditional<ViewState> | When<Vie
467
471
  declare function isWhen<ViewState, Item>(c: Conditional<ViewState> | When<ViewState, any> | ForEach<ViewState, Item> | WithData<ViewState, any> | TextElement<ViewState> | BaseJayElement<ViewState>): c is When<ViewState, any>;
468
472
  declare function isWithData<ViewState, ChildViewState>(c: Conditional<ViewState> | When<ViewState, any> | ForEach<ViewState, any> | WithData<ViewState, ChildViewState> | TextElement<ViewState> | BaseJayElement<ViewState>): c is WithData<ViewState, ChildViewState>;
469
473
  declare function forEach<T, Item>(getItems: (T: any) => Array<Item>, elemCreator: (Item: any) => BaseJayElement<Item>, matchBy: string): ForEach<T, Item>;
474
+ /**
475
+ * Runtime support for pre-rendered slow arrays (slowForEach in jay-html).
476
+ *
477
+ * This function is used when a forEach loop has been pre-rendered at slow phase.
478
+ * The array items are statically embedded in the jay-html, but we still need to:
479
+ * 1. Set up the correct data context for each item (so bindings work)
480
+ * 2. Enable fast/interactive updates within each item
481
+ * 3. Support event handling with the correct item coordinates
482
+ *
483
+ * The key insight is that slowForEachItem switches the data context to the
484
+ * specific array item, so child bindings like {price} work correctly
485
+ * (they resolve to item.price, not viewState.price).
486
+ *
487
+ * @param arrayName - The property name of the array in the parent ViewState
488
+ * @param index - The jayIndex value (position in the pre-rendered array)
489
+ * @param trackByValue - The jayTrackBy value (identity for reconciliation)
490
+ * @param elementCreator - Function that creates the pre-rendered element (called within item context)
491
+ */
492
+ declare function slowForEachItem<ParentVS, ItemVS>(arrayName: keyof ParentVS, index: number, trackByValue: string, elementCreator: () => BaseJayElement<ItemVS>): BaseJayElement<ParentVS>;
470
493
  interface ForEach<ViewState, Item> {
471
494
  getItems: (T: any) => Array<Item>;
472
495
  elemCreator: (Item: any, String: any) => BaseJayElement<Item>;
@@ -550,4 +573,4 @@ declare class ConstructContext<ViewState> {
550
573
  static withRootContext<ViewState, Refs>(viewState: ViewState, refManager: ReferencesManager, elementConstructor: () => BaseJayElement<ViewState>): JayElement<ViewState, Refs>;
551
574
  }
552
575
 
553
- export { type Attribute, type Attributes, type BaseJayElement, BaseReferencesManager, type ComponentCollectionProxy, ComponentCollectionRefImpl, type ComponentProxy, ComponentRefImpl, ComponentRefsImpl, type Conditional, ConstructContext, type ContextMarker, type Coordinate, type DynamicAttributeOrProperty, EVENT_TRAP, type ElementFrom, type EventEmitter, type EventTypeFrom, type ExtractFastViewState, type ExtractInteractiveViewState, type ExtractRefs, type ExtractSlowViewState, type ExtractViewState, type ForEach, GetTrapProxy, type GlobalJayEvents, type HTMLElementCollectionProxy, type HTMLElementCollectionProxyTarget, type HTMLElementProxy, type HTMLElementProxyTarget, type HTMLNativeExec, type HeadLink, type JayComponent, type JayComponentConstructor, type JayContract, type JayElement, type JayEvent, type JayEventHandler, type JayEventHandlerWrapper, type JayLog, type JayNativeEventBuilder, type JayNativeFunction, LogType, type ManagedRefConstructor, ManagedRefType, type ManagedRefs, type MapEventEmitterViewState, type MountFunc, type OnlyEventEmitters, type PreRenderElement, type PrivateRef, type PrivateRefConstructor, PrivateRefs, type PropsFrom, ReferencesManager, type RenderElement, type RenderElementOptions, type TextElement, type ViewStateFrom, type When, WhenRole, type WithData, booleanAttribute, childComp, clearGlobalContextRegistry, conditional, createJayContext, currentConstructionContext, defaultEventWrapper, dynamicAttribute, dynamicElement, dynamicElementNS, dynamicProperty, dynamicText, element, findContext, forEach, injectHeadLinks, isCondition, isForEach, isWhen, isWithData, jayLog, mathMLDynamicElement, mathMLElement, mkUpdateCollection, noop, noopMount, noopUpdate, normalizeMount, normalizeUpdates, pending, registerGlobalContext, rejected, resolved, restoreContext, saveContext, svgDynamicElement, svgElement, type updateFunc, useContext, useGlobalContext, withContext, withData };
576
+ export { type Attribute, type Attributes, type BaseJayElement, BaseReferencesManager, type ComponentCollectionProxy, ComponentCollectionRefImpl, type ComponentProxy, ComponentRefImpl, ComponentRefsImpl, type Conditional, ConstructContext, type ContextMarker, type Coordinate, type DynamicAttributeOrProperty, EVENT_TRAP, type ElementFrom, type EventEmitter, type EventTypeFrom, type ExtractFastViewState, type ExtractInteractiveViewState, type ExtractRefs, type ExtractSlowViewState, type ExtractViewState, type ForEach, GetTrapProxy, type GlobalJayEvents, type HTMLElementCollectionProxy, type HTMLElementCollectionProxyTarget, type HTMLElementProxy, type HTMLElementProxyTarget, type HTMLNativeExec, type HeadLink, type JayComponent, type JayComponentConstructor, type JayContract, type JayElement, type JayEvent, type JayEventHandler, type JayEventHandlerWrapper, type JayLog, type JayNativeEventBuilder, type JayNativeFunction, LogType, type ManagedRefConstructor, ManagedRefType, type ManagedRefs, type MapEventEmitterViewState, type MountFunc, type OnlyEventEmitters, type PreRenderElement, type PrivateRef, type PrivateRefConstructor, PrivateRefs, type PropsFrom, ReferencesManager, type RenderElement, type RenderElementOptions, type TextElement, VIEW_STATE_CHANGE_EVENT, type ViewStateFrom, type When, WhenRole, type WithData, booleanAttribute, childComp, clearGlobalContextRegistry, conditional, createJayContext, currentConstructionContext, defaultEventWrapper, dynamicAttribute, dynamicElement, dynamicElementNS, dynamicProperty, dynamicText, element, findContext, forEach, injectHeadLinks, isCondition, isForEach, isWhen, isWithData, jayLog, mathMLDynamicElement, mathMLElement, mkUpdateCollection, noop, noopMount, noopUpdate, normalizeMount, normalizeUpdates, pending, registerGlobalContext, rejected, resolved, restoreContext, saveContext, slowForEachItem, svgDynamicElement, svgElement, type updateFunc, useContext, useGlobalContext, withContext, withData };
package/dist/index.js CHANGED
@@ -84,6 +84,7 @@ const noop = () => {
84
84
  };
85
85
  const noopUpdate = noop;
86
86
  const noopMount = noop;
87
+ const VIEW_STATE_CHANGE_EVENT = "viewStateChange";
87
88
  var LogType = /* @__PURE__ */ ((LogType2) => {
88
89
  LogType2[LogType2["ASYNC_ERROR"] = 0] = "ASYNC_ERROR";
89
90
  LogType2[LogType2["CONTEXT_NOT_FOUND"] = 1] = "CONTEXT_NOT_FOUND";
@@ -367,6 +368,33 @@ function mkWhenPendingCondition(when2, group) {
367
368
  function forEach(getItems, elemCreator, matchBy) {
368
369
  return { getItems, elemCreator, trackBy: matchBy };
369
370
  }
371
+ function slowForEachItem(arrayName, index, trackByValue, elementCreator) {
372
+ const parentContext = currentConstructionContext();
373
+ const savedContext = saveContext();
374
+ const parentData = parentContext.currData;
375
+ const array = parentData[arrayName];
376
+ const initialItem = array?.[index];
377
+ const childContext = parentContext.forItem(initialItem, trackByValue);
378
+ const element2 = withContext(CONSTRUCTION_CONTEXT_MARKER, childContext, elementCreator);
379
+ const originalUpdate = element2.update;
380
+ const update = (newParentData) => {
381
+ const newArray = newParentData[arrayName];
382
+ const newItem = newArray?.[index];
383
+ const updateChildContext = parentContext.forItem(newItem, trackByValue);
384
+ restoreContext(
385
+ savedContext,
386
+ () => withContext(CONSTRUCTION_CONTEXT_MARKER, updateChildContext, () => {
387
+ originalUpdate(newItem);
388
+ })
389
+ );
390
+ };
391
+ return {
392
+ dom: element2.dom,
393
+ update,
394
+ mount: element2.mount,
395
+ unmount: element2.unmount
396
+ };
397
+ }
370
398
  function withData(accessor, elem) {
371
399
  return { accessor, elem };
372
400
  }
@@ -995,6 +1023,7 @@ export {
995
1023
  ManagedRefType,
996
1024
  PrivateRefs,
997
1025
  ReferencesManager,
1026
+ VIEW_STATE_CHANGE_EVENT,
998
1027
  WhenRole,
999
1028
  booleanAttribute,
1000
1029
  childComp,
@@ -1031,6 +1060,7 @@ export {
1031
1060
  resolved,
1032
1061
  restoreContext,
1033
1062
  saveContext,
1063
+ slowForEachItem,
1034
1064
  svgDynamicElement,
1035
1065
  svgElement,
1036
1066
  useContext,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/runtime",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.js",
@@ -23,11 +23,11 @@
23
23
  "test:watch": "vitest"
24
24
  },
25
25
  "dependencies": {
26
- "@jay-framework/list-compare": "^0.10.0",
27
- "@jay-framework/reactive": "^0.10.0"
26
+ "@jay-framework/list-compare": "^0.11.0",
27
+ "@jay-framework/reactive": "^0.11.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@jay-framework/dev-environment": "^0.10.0",
30
+ "@jay-framework/dev-environment": "^0.11.0",
31
31
  "@testing-library/jest-dom": "^6.2.0",
32
32
  "@types/jsdom": "^21.1.6",
33
33
  "@types/node": "^20.11.5",
package/readme.md CHANGED
@@ -105,7 +105,57 @@ interface JayElement<ViewState, Refs> extends BaseJayElement<ViewState> {
105
105
  These references can be used to set event listeners, interact with child elements or component APIs.
106
106
  Read more about `refs` in [refs.md](./docs/refs.md).
107
107
 
108
- ## implementation details
108
+ ## Slow Rendering Support
109
+
110
+ The runtime supports pre-rendered "slow" arrays via the `slowForEachItem` function. This is used when a `forEach` loop has been pre-rendered at build time (slow phase), but still needs fast/interactive updates at runtime.
111
+
112
+ ### slowForEachItem
113
+
114
+ ```typescript
115
+ function slowForEachItem<ParentVS, ItemVS>(
116
+ arrayName: keyof ParentVS,
117
+ index: number,
118
+ trackByValue: string,
119
+ elementCreator: () => BaseJayElement<ItemVS>,
120
+ ): BaseJayElement<ParentVS>;
121
+ ```
122
+
123
+ **Parameters:**
124
+
125
+ - `arrayName` - The property name of the array in the parent ViewState
126
+ - `index` - The position in the pre-rendered array (jayIndex)
127
+ - `trackByValue` - The track-by value for client reconciliation (jayTrackBy)
128
+ - `elementCreator` - Function that creates the element (called within item context)
129
+
130
+ **How it works:**
131
+
132
+ 1. Sets the data context to `viewState[arrayName][index]`
133
+ 2. Calls `elementCreator()` within that context so bindings are item-scoped
134
+ 3. On update, retrieves the item from the new ViewState and updates with item context
135
+
136
+ **Example generated code:**
137
+
138
+ ```typescript
139
+ slowForEachItem('products', 0, 'p1', () =>
140
+ e('li', {}, [
141
+ e('span', { class: 'name' }, ['Widget A']), // static (pre-rendered)
142
+ e('span', { class: 'price' }, [
143
+ dt((vs) => vs.price), // dynamic (fast phase) - item-scoped
144
+ ]),
145
+ ]),
146
+ );
147
+ ```
148
+
149
+ This corresponds to pre-rendered jay-html:
150
+
151
+ ```html
152
+ <li slowForEach="products" trackBy="id" jayIndex="0" jayTrackBy="p1">
153
+ <span class="name">Widget A</span>
154
+ <span class="price">{price}</span>
155
+ </li>
156
+ ```
157
+
158
+ ## Implementation Details
109
159
 
110
160
  - See the [Generated JayElement](./docs/jay-element.md) for the details of the `jay-html` generated target.
111
161
  - See the [Generated JayElement creation Functions](./docs/runtime.md) for the details of the jay element creation functions used by the `jay-html` generated target.