@jay-framework/component 0.9.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
@@ -41,6 +41,31 @@ declare const forTesting: {
41
41
 
42
42
  declare const CONTEXT_REACTIVE_SYMBOL_CONTEXT: unique symbol;
43
43
  declare function createReactiveContext<T extends object>(mkContext: () => T): T;
44
+ /**
45
+ * Creates a reactive context and registers it globally.
46
+ * Use this in `withClient` initialization to create reactive global contexts.
47
+ *
48
+ * @param marker - The context marker created with createJayContext()
49
+ * @param mkContext - Factory function that creates the context (can use createSignal, etc.)
50
+ * @returns The created context (for immediate use in init)
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * export const init = makeJayInit()
55
+ * .withClient(async () => {
56
+ * const ctx = registerReactiveGlobalContext(MY_CONTEXT, () => {
57
+ * const [count, setCount] = createSignal(0);
58
+ * return {
59
+ * count,
60
+ * increment: () => setCount(n => n + 1),
61
+ * async init() { await loadData(); },
62
+ * };
63
+ * });
64
+ * await ctx.init();
65
+ * });
66
+ * ```
67
+ */
68
+ declare function registerReactiveGlobalContext<T extends object>(marker: ContextMarker<T>, mkContext: () => T): T;
44
69
 
45
70
  type EffectCleanup = () => void;
46
71
  declare function createEffect(effect: () => void | EffectCleanup): void;
@@ -66,4 +91,4 @@ interface ComponentContext extends HookContext {
66
91
  declare const COMPONENT_CONTEXT: ContextMarker<ComponentContext>;
67
92
  declare const CONTEXT_CREATION_CONTEXT: ContextMarker<HookContext>;
68
93
 
69
- export { COMPONENT_CONTEXT, CONTEXT_CREATION_CONTEXT, CONTEXT_REACTIVE_SYMBOL_CONTEXT, type ComponentConstructor, type ComponentContext, type ConcreteJayComponent, type ContextMarkers, type EffectCleanup, type HookContext, type JayComponentCore, type JayTsxComponentConstructor, type JsxNode, type Patcher, type Props, type UpdatableProps, type ViewStateGetters, createDerivedArray, createEffect, createEvent, createMemo, createPatchableSignal, createReactiveContext, createSignal, forTesting, type hasProps, makeJayComponent, makeJayTsxComponent, makePropsProxy, materializeViewState, provideContext, provideReactiveContext, useReactive };
94
+ export { COMPONENT_CONTEXT, CONTEXT_CREATION_CONTEXT, CONTEXT_REACTIVE_SYMBOL_CONTEXT, type ComponentConstructor, type ComponentContext, type ConcreteJayComponent, type ContextMarkers, type EffectCleanup, type HookContext, type JayComponentCore, type JayTsxComponentConstructor, type JsxNode, type Patcher, type Props, type UpdatableProps, type ViewStateGetters, createDerivedArray, createEffect, createEvent, createMemo, createPatchableSignal, createReactiveContext, createSignal, forTesting, type hasProps, makeJayComponent, makeJayTsxComponent, makePropsProxy, materializeViewState, provideContext, provideReactiveContext, registerReactiveGlobalContext, useReactive };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createJayContext, withContext, findContext, useContext } from "@jay-framework/runtime";
1
+ import { createJayContext, withContext, registerGlobalContext, findContext, useContext, VIEW_STATE_CHANGE_EVENT } from "@jay-framework/runtime";
2
2
  import { mkReactive, GetterMark, SetterMark, MeasureOfChange } from "@jay-framework/reactive";
3
3
  import { patch } from "@jay-framework/json-patch";
4
4
  const COMPONENT_CONTEXT = createJayContext();
@@ -34,6 +34,11 @@ function createReactiveContext(mkContext) {
34
34
  );
35
35
  return newContextProxy(reactive, context);
36
36
  }
37
+ function registerReactiveGlobalContext(marker, mkContext) {
38
+ const context = createReactiveContext(mkContext);
39
+ registerGlobalContext(marker, context);
40
+ return context;
41
+ }
37
42
  function currentHookContext() {
38
43
  return findContext((_) => _ === COMPONENT_CONTEXT || _ === CONTEXT_CREATION_CONTEXT);
39
44
  }
@@ -221,9 +226,12 @@ function makeJayComponent(preRender, comp, ...contextMarkers) {
221
226
  componentContext.provideContexts.forEach(
222
227
  ([marker, context]) => context[CONTEXT_REACTIVE_SYMBOL_CONTEXT] && reactive.enablePairing(context[CONTEXT_REACTIVE_SYMBOL_CONTEXT])
223
228
  );
229
+ let currentViewState;
230
+ let viewStateChangeListener;
224
231
  componentContext.reactive.createReaction(() => {
225
232
  let viewStateValueOrGetters = renderViewState();
226
233
  let viewState = materializeViewState(viewStateValueOrGetters);
234
+ currentViewState = viewState;
227
235
  if (!element)
228
236
  element = renderWithContexts(
229
237
  componentContext.provideContexts,
@@ -232,19 +240,24 @@ function makeJayComponent(preRender, comp, ...contextMarkers) {
232
240
  );
233
241
  else
234
242
  element.update(viewState);
243
+ viewStateChangeListener?.({ event: viewState, viewState, coordinate: [] });
235
244
  });
236
245
  const [mount, unmount] = mkMounts(componentContext, element);
237
246
  let update = (updateProps) => {
238
247
  propsProxy.update(updateProps);
239
248
  };
240
- let events = {};
249
+ let events = {
250
+ [VIEW_STATE_CHANGE_EVENT]: (handler) => {
251
+ viewStateChangeListener = handler;
252
+ }
253
+ };
241
254
  let component = {
242
255
  element,
243
256
  update,
244
257
  mount,
245
258
  unmount,
246
- addEventListener: (eventType, handler) => events[eventType](handler),
247
- removeEventListener: (eventType) => events[eventType](void 0)
259
+ addEventListener: (eventType, handler) => events[eventType]?.(handler),
260
+ removeEventListener: (eventType) => events[eventType]?.(void 0)
248
261
  };
249
262
  for (let key in api) {
250
263
  if (typeof api[key] === "function") {
@@ -260,6 +273,11 @@ function makeJayComponent(preRender, comp, ...contextMarkers) {
260
273
  component[key] = api[key];
261
274
  }
262
275
  }
276
+ Object.defineProperty(component, "viewState", {
277
+ get: () => currentViewState,
278
+ enumerable: false,
279
+ configurable: true
280
+ });
263
281
  return componentInstance = component;
264
282
  });
265
283
  };
@@ -318,5 +336,6 @@ export {
318
336
  materializeViewState,
319
337
  provideContext,
320
338
  provideReactiveContext,
339
+ registerReactiveGlobalContext,
321
340
  useReactive
322
341
  };
@@ -0,0 +1,128 @@
1
+ # registerReactiveGlobalContext
2
+
3
+ Creates a reactive context and registers it globally for use across the application.
4
+
5
+ Use this in `withClient` initialization (via `makeJayInit`) to create reactive global contexts
6
+ that are available to all components without needing a parent component to provide them.
7
+
8
+ ```typescript
9
+ declare function registerReactiveGlobalContext<T extends object>(
10
+ marker: ContextMarker<T>,
11
+ mkContext: () => T,
12
+ ): T;
13
+ ```
14
+
15
+ ## Parameters:
16
+
17
+ - `marker`: A unique symbol identifying the context, created using `createJayContext`.
18
+ - `mkContext`: A function that creates the initial context value.
19
+ The function may use any of the Jay Hooks (`createSignal`, `createEffect`, etc.) and should return an object which represents the context.
20
+
21
+ ## Returns:
22
+
23
+ The created reactive context value, which can be used immediately in the init function.
24
+
25
+ ## Behavior
26
+
27
+ - Creates a `Reactive` instance for signal tracking
28
+ - Sets up the context creation environment so `createSignal()` and other hooks work
29
+ - Returns a proxy that batches reactions for function calls (same as `provideReactiveContext`)
30
+ - Registers the context in the global registry so it's available via `useContext()`
31
+
32
+ ## Examples:
33
+
34
+ ### Basic Counter Context
35
+
36
+ ```typescript
37
+ import { createJayContext } from '@jay-framework/runtime';
38
+ import { createSignal, registerReactiveGlobalContext } from '@jay-framework/component';
39
+ import { makeJayInit } from '@jay-framework/fullstack-component';
40
+
41
+ interface CounterContext {
42
+ count: () => number;
43
+ increment: () => void;
44
+ }
45
+
46
+ export const COUNTER_CTX = createJayContext<CounterContext>();
47
+
48
+ export const init = makeJayInit().withClient(() => {
49
+ registerReactiveGlobalContext(COUNTER_CTX, () => {
50
+ const [count, setCount] = createSignal(0);
51
+ return {
52
+ count,
53
+ increment: () => setCount((n) => n + 1),
54
+ };
55
+ });
56
+ });
57
+ ```
58
+
59
+ ### Async Initialization Pattern
60
+
61
+ For contexts that need async initialization, expose an `init()` method:
62
+
63
+ ```typescript
64
+ interface WixClientContext {
65
+ client: WixClient;
66
+ isReady: () => boolean;
67
+ init: () => Promise<void>;
68
+ }
69
+
70
+ export const WIX_CLIENT_CTX = createJayContext<WixClientContext>();
71
+
72
+ export const init = makeJayInit().withClient(async () => {
73
+ const ctx = registerReactiveGlobalContext(WIX_CLIENT_CTX, () => {
74
+ const [isReady, setIsReady] = createSignal(false);
75
+
76
+ return {
77
+ client: wixClient,
78
+ isReady,
79
+ async init() {
80
+ const tokens = await wixClient.auth.generateVisitorTokens();
81
+ wixClient.auth.setTokens(tokens);
82
+ setIsReady(true);
83
+ },
84
+ };
85
+ });
86
+
87
+ // Call init immediately
88
+ await ctx.init();
89
+ });
90
+ ```
91
+
92
+ ### Using the Context in Components
93
+
94
+ ```typescript
95
+ import { useContext } from '@jay-framework/runtime';
96
+ import { COUNTER_CTX } from '../lib/init';
97
+
98
+ function MyComponent(props, refs) {
99
+ // Access the global context
100
+ const counter = useContext(COUNTER_CTX);
101
+
102
+ refs.incrementButton.onclick(() => {
103
+ counter.increment();
104
+ });
105
+
106
+ return {
107
+ render: () => ({
108
+ count: counter.count(),
109
+ }),
110
+ };
111
+ }
112
+ ```
113
+
114
+ ## Comparison with provideReactiveContext
115
+
116
+ | Feature | `provideReactiveContext` | `registerReactiveGlobalContext` |
117
+ | ------------- | -------------------------------------------- | ------------------------------- |
118
+ | Scope | Component subtree | Entire application |
119
+ | When to use | Component provides context to children | App initialization |
120
+ | Where to call | Inside component constructor | Inside `withClient` |
121
+ | Override | Child components can provide different value | Cannot be overridden |
122
+
123
+ ## Design Log
124
+
125
+ For additional information on the design decisions, read:
126
+
127
+ - [67 - registerReactiveGlobalContext.md](../../../../design-log/67%20-%20registerReactiveGlobalContext.md)
128
+ - [65 - makeJayInit builder pattern.md](../../../../design-log/65%20-%20makeJayInit%20builder%20pattern.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/component",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.js",
@@ -21,12 +21,12 @@
21
21
  "test:watch": "vitest"
22
22
  },
23
23
  "dependencies": {
24
- "@jay-framework/json-patch": "^0.9.0",
25
- "@jay-framework/reactive": "^0.9.0",
26
- "@jay-framework/runtime": "^0.9.0"
24
+ "@jay-framework/json-patch": "^0.11.0",
25
+ "@jay-framework/reactive": "^0.11.0",
26
+ "@jay-framework/runtime": "^0.11.0"
27
27
  },
28
28
  "devDependencies": {
29
- "@jay-framework/dev-environment": "^0.9.0",
29
+ "@jay-framework/dev-environment": "^0.11.0",
30
30
  "@testing-library/jest-dom": "^6.2.0",
31
31
  "@types/node": "^20.11.5",
32
32
  "rimraf": "^5.0.5",
package/readme.md CHANGED
@@ -27,6 +27,7 @@ Hooks for providing context:
27
27
 
28
28
  - [provideContext](./docs/provide-context.md)
29
29
  - [provideReactiveContext](./docs/provide-reactive-context.md)
30
+ - [registerReactiveGlobalContext](./docs/register-reactive-global-context.md)
30
31
 
31
32
  Hooks to get Component Reactive Instance:
32
33