@jay-framework/4-react 0.5.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.
@@ -0,0 +1,43 @@
1
+ import { JayEventHandler, Coordinate, JayEventHandlerWrapper, JayElement, PreRenderElement, OnlyEventEmitters, EventEmitter } from '@jay-framework/runtime';
2
+ import * as React from 'react';
3
+ import { Getter } from '@jay-framework/reactive';
4
+
5
+ type eventType = string;
6
+ type refName = string;
7
+ type JayReactElementEvents = Record<eventType, JayEventHandler<any, any, any>>;
8
+ type JayReactEvents = Record<refName, JayReactElementEvents>;
9
+ declare class EventsContext {
10
+ readonly viewState: object;
11
+ readonly coordinateBase: Coordinate;
12
+ readonly eventsWrapper: JayEventHandlerWrapper<any, any, any>;
13
+ private reactEvents;
14
+ constructor(viewState: object, coordinateBase: Coordinate, eventsWrapper: JayEventHandlerWrapper<any, any, any>, reactEvents: JayReactEvents);
15
+ coordinate(refName: string): Coordinate;
16
+ events(refName: string): JayReactElementEvents;
17
+ child(id: string, viewState: object): EventsContext;
18
+ withViewState(viewState: object): EventsContext;
19
+ }
20
+ declare function eventsFor<ViewState>(eventsContext: EventsContext, refName: string): {};
21
+ declare function refsRecorder<Refs>(eventWrapper?: JayEventHandlerWrapper<any, any, any>): [Refs, EventsContext];
22
+
23
+ interface Jay4ReactElementProps<ViewState> {
24
+ vs: ViewState;
25
+ context: EventsContext;
26
+ }
27
+ type EventEmittersToReactCallbacks<T> = {
28
+ [Key in keyof T as T[Key] extends EventEmitter<any, any> ? Key : never]?: T[Key] extends EventEmitter<infer EventType, any> ? (event: EventType) => void : T[Key];
29
+ };
30
+ type Jay2React<Comp extends (...args: any) => any> = Parameters<Comp>[0] & EventEmittersToReactCallbacks<OnlyEventEmitters<ReturnType<Comp>>>;
31
+ declare function jay2React<PropsT extends object, ViewState extends object, Refs extends object, JayElementT extends JayElement<ViewState, Refs>, CompConstructor extends (...args: any) => any>(comp: Getter<CompConstructor>): React.FC<Jay2React<CompConstructor>>;
32
+ declare function mimicJayElement<ViewState extends object, Refs extends object, JayElementT extends JayElement<ViewState, Refs>, ElementProps extends Jay4ReactElementProps<ViewState>>(reactElement: React.FC<ElementProps>): PreRenderElement<ViewState, Refs, JayElementT>;
33
+
34
+ type PropsFromReact<T extends (...args: any[]) => any> = Parameters<T>[0];
35
+ type FunctionProperties<T> = {
36
+ [K in keyof T]: T[K] extends (...args: any[]) => any ? T[K] : never;
37
+ };
38
+ type OnlyFunctionProperties2<A> = {
39
+ [K in keyof A as A[K] extends never ? never : K]: A[K];
40
+ };
41
+ type ReactToCompRef<T extends (...args: any[]) => any> = OnlyFunctionProperties2<FunctionProperties<PropsFromReact<T>>>;
42
+
43
+ export { EventsContext, type Jay4ReactElementProps, type JayReactElementEvents, type JayReactEvents, type ReactToCompRef, eventsFor, jay2React, mimicJayElement, refsRecorder };
package/dist/index.js ADDED
@@ -0,0 +1,172 @@
1
+ import { useRef, useState } from "react";
2
+ function uppercaseThirdLetter(str) {
3
+ if (str.length >= 3 && str.substring(0, 2) === "on") {
4
+ return str.substring(0, 2) + str[2].toUpperCase() + str.substring(3);
5
+ } else {
6
+ return str;
7
+ }
8
+ }
9
+ const SPECIAL_JAY_TO_REACT_EVENT_NAMES = {
10
+ onkeydown: "onKeyDown",
11
+ ondblclick: "onDoubleClick"
12
+ };
13
+ function jayToReactEventName(jayEventName) {
14
+ return SPECIAL_JAY_TO_REACT_EVENT_NAMES[jayEventName] || uppercaseThirdLetter(jayEventName);
15
+ }
16
+ class EventsContext {
17
+ constructor(viewState, coordinateBase, eventsWrapper, reactEvents) {
18
+ this.viewState = viewState;
19
+ this.coordinateBase = coordinateBase;
20
+ this.eventsWrapper = eventsWrapper;
21
+ this.reactEvents = reactEvents;
22
+ }
23
+ coordinate(refName) {
24
+ return [...this.coordinateBase, refName];
25
+ }
26
+ events(refName) {
27
+ return this.reactEvents[refName];
28
+ }
29
+ child(id, viewState) {
30
+ return new EventsContext(
31
+ viewState,
32
+ [...this.coordinateBase, id],
33
+ this.eventsWrapper,
34
+ this.reactEvents
35
+ );
36
+ }
37
+ withViewState(viewState) {
38
+ return new EventsContext(
39
+ viewState,
40
+ this.coordinateBase,
41
+ this.eventsWrapper,
42
+ this.reactEvents
43
+ );
44
+ }
45
+ }
46
+ function eventsFor(eventsContext, refName) {
47
+ const reactCallbacks = {};
48
+ const events = eventsContext.events(refName);
49
+ Object.entries(events).forEach((event) => {
50
+ const name = jayToReactEventName(event[0]);
51
+ const handler = event[1];
52
+ reactCallbacks[name] = (reactEvent) => {
53
+ const coordinate = eventsContext.coordinate(refName);
54
+ const viewState = eventsContext.viewState;
55
+ const eventsWrapper = eventsContext.eventsWrapper;
56
+ const jayEvent = {
57
+ viewState,
58
+ coordinate,
59
+ event: reactEvent?.nativeEvent ? reactEvent.nativeEvent : reactEvent
60
+ };
61
+ if (eventsWrapper)
62
+ eventsWrapper(handler, jayEvent);
63
+ else
64
+ handler(jayEvent);
65
+ };
66
+ });
67
+ return reactCallbacks;
68
+ }
69
+ function refRecorder() {
70
+ const ref = {};
71
+ const proxy = new Proxy(
72
+ {},
73
+ {
74
+ get(target, prop, receiver) {
75
+ return (arg) => ref[prop] = arg;
76
+ }
77
+ }
78
+ );
79
+ return [proxy, ref];
80
+ }
81
+ function refsRecorder(eventWrapper) {
82
+ const refs = {};
83
+ const events = {};
84
+ const _0 = new Proxy(
85
+ {},
86
+ {
87
+ get(target, p, receiver) {
88
+ if (refs[p]) {
89
+ return refs[p];
90
+ } else {
91
+ const [proxy, ref] = refRecorder();
92
+ events[p] = ref;
93
+ return refs[p] = proxy;
94
+ }
95
+ }
96
+ }
97
+ );
98
+ const _1 = new EventsContext(null, [], eventWrapper, events);
99
+ return [_0, _1];
100
+ }
101
+ function splitPropsEvents(reactProps) {
102
+ const props = {}, events = {};
103
+ Object.keys(reactProps).forEach((key) => {
104
+ if (reactProps[key] instanceof Function)
105
+ events[key] = reactProps[key];
106
+ else
107
+ props[key] = reactProps[key];
108
+ });
109
+ return [props, events];
110
+ }
111
+ function jay2React(comp) {
112
+ return (reactProps) => {
113
+ const [props, events] = splitPropsEvents(reactProps);
114
+ const myInstanceRef = useRef(null);
115
+ if (!myInstanceRef.current) {
116
+ myInstanceRef.current = comp()(props);
117
+ } else {
118
+ const reactViewStateState = useState(null);
119
+ myInstanceRef.current.update({ ...props, reactViewStateState });
120
+ }
121
+ Object.keys(events).forEach(
122
+ (eventName) => myInstanceRef.current.addEventListener(
123
+ eventName.substring(2),
124
+ ({ event }) => events[eventName](event)
125
+ )
126
+ );
127
+ return myInstanceRef.current.element["react"];
128
+ };
129
+ }
130
+ function mimicJayElement(reactElement) {
131
+ const preRender = (options) => {
132
+ const [refs, eventsContext] = refsRecorder(options.eventWrapper);
133
+ let viewState, setViewState;
134
+ return [
135
+ refs,
136
+ (vs) => {
137
+ const _eventsContext = eventsContext.withViewState(vs);
138
+ [viewState, setViewState] = useState(vs);
139
+ let react = reactElement({
140
+ vs,
141
+ context: _eventsContext
142
+ });
143
+ const element = {
144
+ update: (newData) => {
145
+ element["react"] = reactElement({
146
+ vs: newData,
147
+ context: _eventsContext
148
+ });
149
+ if (newData["reactViewStateState"])
150
+ [viewState, setViewState] = newData["reactViewStateState"];
151
+ setViewState(newData);
152
+ },
153
+ mount: () => {
154
+ },
155
+ unmount: () => {
156
+ },
157
+ refs,
158
+ react
159
+ };
160
+ return element;
161
+ }
162
+ ];
163
+ };
164
+ return preRender;
165
+ }
166
+ export {
167
+ EventsContext,
168
+ eventsFor,
169
+ jay2React,
170
+ mimicJayElement,
171
+ refsRecorder
172
+ };
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@jay-framework/4-react",
3
+ "version": "0.5.0",
4
+ "type": "module",
5
+ "license": "Apache-2.0",
6
+ "main": "dist/index.js",
7
+ "files": [
8
+ "dist",
9
+ "readme.md"
10
+ ],
11
+ "scripts": {
12
+ "build": "npm run build:js && npm run build:types",
13
+ "build:watch": "npm run build:cli-link && npm run build:js -- --watch",
14
+ "build:js": "vite build",
15
+ "build:types": "tsup lib/index.ts --dts-only --format esm",
16
+ "build:check-types": "tsc",
17
+ "clean": "rimraf dist",
18
+ "confirm": "npm run clean && npm run build && npm run build:check-types && npm run test && npm run definitions && npm run runtime",
19
+ "test": "vitest run",
20
+ "test:watch": "vitest"
21
+ },
22
+ "dependencies": {
23
+ "@jay-framework/component": "workspace:^",
24
+ "@jay-framework/json-patch": "workspace:^",
25
+ "@jay-framework/reactive": "workspace:^",
26
+ "@jay-framework/runtime": "workspace:^",
27
+ "@jay-framework/secure": "workspace:^",
28
+ "@jay-framework/serialization": "workspace:^",
29
+ "@testing-library/dom": "^10.4.0",
30
+ "react": "^18.2.0",
31
+ "react-dom": "^18.2.0"
32
+ },
33
+ "devDependencies": {
34
+ "@jay-framework/dev-environment": "workspace:^",
35
+ "@testing-library/jest-dom": "^6.4.2",
36
+ "@testing-library/react": "^14.2.2",
37
+ "@testing-library/user-event": "^14.5.2",
38
+ "@types/node": "^20.11.5",
39
+ "@types/react": "^18",
40
+ "@types/react-dom": "^18",
41
+ "@vitejs/plugin-react": "^4.2.1",
42
+ "caniuse-lite": "^1.0.30001579",
43
+ "npm-run-all2": "^6.1.1",
44
+ "rimraf": "^5.0.5",
45
+ "tsup": "^8.0.1",
46
+ "typescript": "^5.3.3",
47
+ "vite": "^5.0.11",
48
+ "vitest": "^1.2.1"
49
+ }
50
+ }
package/readme.md ADDED
@@ -0,0 +1,36 @@
1
+ # Jay 4 React
2
+
3
+ The Jay 4 React runtime module is an adapter module that enables running Jay Components "as is" with React Code.
4
+
5
+ - A Jay Component runs "as is"
6
+ - A Jay Element is compiled into regular functional React component
7
+ - The `jay2React` and `mimicJayElement` adapt between Jay and React
8
+
9
+ The main principle is that the Jay development experience is not changed, while the only thing that changes is now
10
+ we compile the `jay-html` files into Jay Elements. This runtime library facilitates this process.
11
+
12
+ Using those function require compiling Jay with compilation target React.
13
+ See the [compiler](../../compiler/compiler) for more details on compile target.
14
+
15
+ ## jay2React
16
+
17
+ The function `Jay2React` is an adapter function turning a Jay Component into a React component.
18
+ It is used in React code seeking to import Jay Components, as well as in generated Jay Elements
19
+ who have child Jay Components.
20
+
21
+ ```typescript
22
+ const ReactCart = jay2React(() => Cart);
23
+ ```
24
+
25
+ Where `Cart` is a regular Jay Component, and `ReactCart` is a React component.
26
+
27
+ The function transforms Jay the Jay Component as
28
+
29
+ - Jay Props become React Props
30
+ - Jay Events become callbacks in React Props
31
+ - Jay APIs are not exposed in React (as React has no notion of component APIs)
32
+
33
+ ## mimicJayElement
34
+
35
+ The function `mimicJayElement` is an adapter function turning a React Component into a Jay Element,
36
+ to be used with React Components.