@reactpy/client 0.1.0 → 0.2.1
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/components.d.ts +10 -0
- package/dist/components.d.ts.map +1 -0
- package/dist/components.js +172 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/logger.d.ts +7 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +5 -0
- package/dist/messages.d.ts +24 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +1 -0
- package/dist/mount.d.ts +3 -0
- package/dist/mount.d.ts.map +1 -0
- package/dist/mount.js +6 -0
- package/dist/reactpy-client.d.ts +86 -0
- package/dist/reactpy-client.d.ts.map +1 -0
- package/dist/reactpy-client.js +133 -0
- package/dist/reactpy-vdom.d.ts +54 -0
- package/dist/reactpy-vdom.d.ts.map +1 -0
- package/dist/reactpy-vdom.js +140 -0
- package/package.json +22 -20
- package/src/components.tsx +231 -0
- package/src/index.ts +5 -0
- package/src/logger.ts +5 -0
- package/src/messages.ts +32 -0
- package/src/mount.tsx +8 -0
- package/src/reactpy-client.ts +274 -0
- package/src/reactpy-vdom.tsx +261 -0
- package/tsconfig.json +14 -0
- package/src/components.js +0 -220
- package/src/contexts.js +0 -6
- package/src/element-utils.js +0 -82
- package/src/event-to-object.js +0 -240
- package/src/import-source.js +0 -134
- package/src/index.js +0 -4
- package/src/mount.js +0 -105
- package/src/server.js +0 -46
package/src/components.js
DELETED
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import ReactDOM from "react-dom";
|
|
3
|
-
import htm from "htm";
|
|
4
|
-
import { set as setJsonPointer } from "json-pointer";
|
|
5
|
-
|
|
6
|
-
import { useImportSource } from "./import-source.js";
|
|
7
|
-
import { LayoutContext } from "./contexts.js";
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
createElementAttributes,
|
|
11
|
-
createElementChildren,
|
|
12
|
-
} from "./element-utils.js";
|
|
13
|
-
|
|
14
|
-
const html = htm.bind(React.createElement);
|
|
15
|
-
|
|
16
|
-
export function Layout({ saveUpdateHook, sendEvent, loadImportSource }) {
|
|
17
|
-
const currentModel = React.useState({})[0];
|
|
18
|
-
const forceUpdate = useForceUpdate();
|
|
19
|
-
|
|
20
|
-
const patchModel = React.useCallback(
|
|
21
|
-
({ path, model }) => {
|
|
22
|
-
if (!path) {
|
|
23
|
-
Object.assign(currentModel, model);
|
|
24
|
-
} else {
|
|
25
|
-
setJsonPointer(currentModel, path, model);
|
|
26
|
-
}
|
|
27
|
-
forceUpdate();
|
|
28
|
-
},
|
|
29
|
-
[currentModel]
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
React.useEffect(() => saveUpdateHook(patchModel), [patchModel]);
|
|
33
|
-
|
|
34
|
-
if (!Object.keys(currentModel).length) {
|
|
35
|
-
return html`<${React.Fragment} />`;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return html`
|
|
39
|
-
<${LayoutContext.Provider} value=${{ sendEvent, loadImportSource }}>
|
|
40
|
-
<${Element} model=${currentModel} />
|
|
41
|
-
<//>
|
|
42
|
-
`;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function Element({ model }) {
|
|
46
|
-
if (model.error !== undefined) {
|
|
47
|
-
if (model.error) {
|
|
48
|
-
return html`<pre>${model.error}</pre>`;
|
|
49
|
-
} else {
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
} else if (model.tagName == "script") {
|
|
53
|
-
return html`<${ScriptElement} model=${model} />`;
|
|
54
|
-
} else if (["input", "select", "textarea"].includes(model.tagName)) {
|
|
55
|
-
return html`<${UserInputElement} model=${model} />`;
|
|
56
|
-
} else if (model.importSource) {
|
|
57
|
-
return html`<${ImportedElement} model=${model} />`;
|
|
58
|
-
} else {
|
|
59
|
-
return html`<${StandardElement} model=${model} />`;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function StandardElement({ model }) {
|
|
64
|
-
const layoutContext = React.useContext(LayoutContext);
|
|
65
|
-
|
|
66
|
-
let type;
|
|
67
|
-
if (model.tagName == "") {
|
|
68
|
-
type = React.Fragment;
|
|
69
|
-
} else {
|
|
70
|
-
type = model.tagName;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Use createElement here to avoid warning about variable numbers of children not
|
|
74
|
-
// having keys. Warning about this must now be the responsibility of the server
|
|
75
|
-
// providing the models instead of the client rendering them.
|
|
76
|
-
return React.createElement(
|
|
77
|
-
type,
|
|
78
|
-
createElementAttributes(model, layoutContext.sendEvent),
|
|
79
|
-
...createElementChildren(
|
|
80
|
-
model,
|
|
81
|
-
(model) => html`<${Element} key=${model.key} model=${model} />`
|
|
82
|
-
)
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Element with a value attribute controlled by user input
|
|
87
|
-
function UserInputElement({ model }) {
|
|
88
|
-
const ref = React.useRef();
|
|
89
|
-
const layoutContext = React.useContext(LayoutContext);
|
|
90
|
-
|
|
91
|
-
const props = createElementAttributes(model, layoutContext.sendEvent);
|
|
92
|
-
|
|
93
|
-
// Because we handle events asynchronously, we must leave the value uncontrolled in
|
|
94
|
-
// order to allow all changes committed by the user to be recorded in the order they
|
|
95
|
-
// occur. If we don't the user may commit multiple changes before we render next
|
|
96
|
-
// causing the content of prior changes to be overwritten by subsequent changes.
|
|
97
|
-
let value = props.value;
|
|
98
|
-
delete props.value;
|
|
99
|
-
|
|
100
|
-
// Instead of controlling the value, we set it in an effect.
|
|
101
|
-
React.useEffect(() => {
|
|
102
|
-
if (value !== undefined) {
|
|
103
|
-
ref.current.value = value;
|
|
104
|
-
}
|
|
105
|
-
}, [ref.current, value]);
|
|
106
|
-
|
|
107
|
-
// Track a buffer of observed values in order to avoid flicker
|
|
108
|
-
const observedValues = React.useState([])[0];
|
|
109
|
-
if (observedValues) {
|
|
110
|
-
if (value === observedValues[0]) {
|
|
111
|
-
observedValues.shift();
|
|
112
|
-
value = observedValues[observedValues.length - 1];
|
|
113
|
-
} else {
|
|
114
|
-
observedValues.length = 0;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const givenOnChange = props.onChange;
|
|
119
|
-
if (typeof givenOnChange === "function") {
|
|
120
|
-
props.onChange = (event) => {
|
|
121
|
-
observedValues.push(event.target.value);
|
|
122
|
-
givenOnChange(event);
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Use createElement here to avoid warning about variable numbers of children not
|
|
127
|
-
// having keys. Warning about this must now be the responsibility of the server
|
|
128
|
-
// providing the models instead of the client rendering them.
|
|
129
|
-
return React.createElement(
|
|
130
|
-
model.tagName,
|
|
131
|
-
{
|
|
132
|
-
...props,
|
|
133
|
-
ref: (target) => {
|
|
134
|
-
ref.current = target;
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
...createElementChildren(
|
|
138
|
-
model,
|
|
139
|
-
(model) => html`<${Element} key=${model.key} model=${model} />`
|
|
140
|
-
)
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function ScriptElement({ model }) {
|
|
145
|
-
const ref = React.useRef();
|
|
146
|
-
React.useEffect(() => {
|
|
147
|
-
if (model?.children?.length > 1) {
|
|
148
|
-
console.error("Too many children for 'script' element.");
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
let scriptContent = model?.children?.[0];
|
|
152
|
-
|
|
153
|
-
let scriptElement;
|
|
154
|
-
if (model.attributes) {
|
|
155
|
-
scriptElement = document.createElement("script");
|
|
156
|
-
for (const [k, v] of Object.entries(model.attributes)) {
|
|
157
|
-
scriptElement.setAttribute(k, v);
|
|
158
|
-
}
|
|
159
|
-
scriptElement.appendChild(document.createTextNode(scriptContent));
|
|
160
|
-
ref.current.appendChild(scriptElement);
|
|
161
|
-
} else {
|
|
162
|
-
let scriptResult = eval(scriptContent);
|
|
163
|
-
if (typeof scriptResult == "function") {
|
|
164
|
-
return scriptResult();
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}, [model.key]);
|
|
168
|
-
return html`<div ref=${ref} />`;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function ImportedElement({ model }) {
|
|
172
|
-
const layoutContext = React.useContext(LayoutContext);
|
|
173
|
-
|
|
174
|
-
const importSourceFallback = model.importSource.fallback;
|
|
175
|
-
const importSource = useImportSource(model.importSource);
|
|
176
|
-
|
|
177
|
-
if (!importSource) {
|
|
178
|
-
// display a fallback if one was given
|
|
179
|
-
if (!importSourceFallback) {
|
|
180
|
-
return html`<div />`;
|
|
181
|
-
} else if (typeof importSourceFallback === "string") {
|
|
182
|
-
return html`<div>${importSourceFallback}</div>`;
|
|
183
|
-
} else {
|
|
184
|
-
return html`<${StandardElement} model=${importSourceFallback} />`;
|
|
185
|
-
}
|
|
186
|
-
} else {
|
|
187
|
-
return html`<${_ImportedElement}
|
|
188
|
-
model=${model}
|
|
189
|
-
importSource=${importSource}
|
|
190
|
-
/>`;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function _ImportedElement({ model, importSource }) {
|
|
195
|
-
const layoutContext = React.useContext(LayoutContext);
|
|
196
|
-
const mountPoint = React.useRef(null);
|
|
197
|
-
const sourceBinding = React.useRef(null);
|
|
198
|
-
|
|
199
|
-
React.useEffect(() => {
|
|
200
|
-
sourceBinding.current = importSource.bind(mountPoint.current);
|
|
201
|
-
if (!importSource.data.unmountBeforeUpdate) {
|
|
202
|
-
return sourceBinding.current.unmount;
|
|
203
|
-
}
|
|
204
|
-
}, []);
|
|
205
|
-
|
|
206
|
-
// this effect must run every time in case the model has changed
|
|
207
|
-
React.useEffect(() => {
|
|
208
|
-
sourceBinding.current.render(model);
|
|
209
|
-
if (importSource.data.unmountBeforeUpdate) {
|
|
210
|
-
return sourceBinding.current.unmount;
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
return html`<div ref=${mountPoint} />`;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function useForceUpdate() {
|
|
218
|
-
const [, updateState] = React.useState();
|
|
219
|
-
return React.useCallback(() => updateState({}), []);
|
|
220
|
-
}
|
package/src/contexts.js
DELETED
package/src/element-utils.js
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { serializeEvent } from "./event-to-object.js";
|
|
2
|
-
|
|
3
|
-
export function createElementChildren(model, createElement) {
|
|
4
|
-
if (!model.children) {
|
|
5
|
-
return [];
|
|
6
|
-
} else {
|
|
7
|
-
return model.children
|
|
8
|
-
.filter((x) => x) // filter nulls
|
|
9
|
-
.map((child) => {
|
|
10
|
-
switch (typeof child) {
|
|
11
|
-
case "object":
|
|
12
|
-
return createElement(child);
|
|
13
|
-
case "string":
|
|
14
|
-
return child;
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function createElementAttributes(model, sendEvent) {
|
|
21
|
-
const attributes = Object.assign({}, model.attributes);
|
|
22
|
-
|
|
23
|
-
if (model.eventHandlers) {
|
|
24
|
-
for (const [eventName, eventSpec] of Object.entries(model.eventHandlers)) {
|
|
25
|
-
attributes[eventName] = createEventHandler(sendEvent, eventSpec);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return Object.fromEntries(Object.entries(attributes).map(normalizeAttribute));
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function createEventHandler(sendEvent, eventSpec) {
|
|
33
|
-
return function () {
|
|
34
|
-
const data = Array.from(arguments).map((value) => {
|
|
35
|
-
if (typeof value === "object" && value.nativeEvent) {
|
|
36
|
-
if (eventSpec["preventDefault"]) {
|
|
37
|
-
value.preventDefault();
|
|
38
|
-
}
|
|
39
|
-
if (eventSpec["stopPropagation"]) {
|
|
40
|
-
value.stopPropagation();
|
|
41
|
-
}
|
|
42
|
-
return serializeEvent(value);
|
|
43
|
-
} else {
|
|
44
|
-
return value;
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
sendEvent({
|
|
48
|
-
data: data,
|
|
49
|
-
target: eventSpec["target"],
|
|
50
|
-
});
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function normalizeAttribute([key, value]) {
|
|
55
|
-
let normKey = key;
|
|
56
|
-
let normValue = value;
|
|
57
|
-
|
|
58
|
-
if (key === "style" && typeof value === "object") {
|
|
59
|
-
normValue = Object.fromEntries(
|
|
60
|
-
Object.entries(value).map(([k, v]) => [snakeToCamel(k), v])
|
|
61
|
-
);
|
|
62
|
-
} else if (
|
|
63
|
-
key.startsWith("data_") ||
|
|
64
|
-
key.startsWith("aria_") ||
|
|
65
|
-
DASHED_HTML_ATTRS.includes(key)
|
|
66
|
-
) {
|
|
67
|
-
normKey = key.replaceAll("_", "-");
|
|
68
|
-
} else {
|
|
69
|
-
normKey = snakeToCamel(key);
|
|
70
|
-
}
|
|
71
|
-
return [normKey, normValue];
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function snakeToCamel(str) {
|
|
75
|
-
return str.replace(/([_][a-z])/g, (group) =>
|
|
76
|
-
group.toUpperCase().replace("_", "")
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// see list of HTML attributes with dashes in them:
|
|
81
|
-
// https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes#attribute_list
|
|
82
|
-
const DASHED_HTML_ATTRS = ["accept_charset", "http_equiv"];
|
package/src/event-to-object.js
DELETED
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
export function serializeEvent(event) {
|
|
2
|
-
const data = {};
|
|
3
|
-
|
|
4
|
-
if (event.type in eventTransforms) {
|
|
5
|
-
Object.assign(data, eventTransforms[event.type](event));
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
data.target = serializeDomElement(event.target);
|
|
9
|
-
data.currentTarget =
|
|
10
|
-
event.target === event.currentTarget
|
|
11
|
-
? data.target
|
|
12
|
-
: serializeDomElement(event.currentTarget);
|
|
13
|
-
data.relatedTarget = serializeDomElement(event.relatedTarget);
|
|
14
|
-
|
|
15
|
-
return data;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function serializeDomElement(element) {
|
|
19
|
-
let elementData = null;
|
|
20
|
-
if (element) {
|
|
21
|
-
elementData = defaultElementTransform(element);
|
|
22
|
-
if (element.tagName in elementTransforms) {
|
|
23
|
-
elementTransforms[element.tagName].forEach((trans) =>
|
|
24
|
-
Object.assign(elementData, trans(element))
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return elementData;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const elementTransformCategories = {
|
|
32
|
-
hasValue: (element) => ({
|
|
33
|
-
value: element.value,
|
|
34
|
-
}),
|
|
35
|
-
hasCurrentTime: (element) => ({
|
|
36
|
-
currentTime: element.currentTime,
|
|
37
|
-
}),
|
|
38
|
-
hasFiles: (element) => {
|
|
39
|
-
if (element?.type === "file") {
|
|
40
|
-
return {
|
|
41
|
-
files: Array.from(element.files).map((file) => ({
|
|
42
|
-
lastModified: file.lastModified,
|
|
43
|
-
name: file.name,
|
|
44
|
-
size: file.size,
|
|
45
|
-
type: file.type,
|
|
46
|
-
})),
|
|
47
|
-
};
|
|
48
|
-
} else {
|
|
49
|
-
return {};
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
hasElements: (element) => {
|
|
53
|
-
const { elements } = element;
|
|
54
|
-
const indices = [...Array(elements.length).keys()];
|
|
55
|
-
return {
|
|
56
|
-
elements: indices.map((index) => serializeDomElement(elements[index])),
|
|
57
|
-
};
|
|
58
|
-
},
|
|
59
|
-
hasName: (element) => {
|
|
60
|
-
const { name } = element;
|
|
61
|
-
// In some edge cases, "name" may not be a string. For example, in the case of
|
|
62
|
-
// `<form><input name="name"></form>`, the "name" attribute of the `<form>` will
|
|
63
|
-
// be the `<input>` element.
|
|
64
|
-
return typeof name === "string" ? { name } : {};
|
|
65
|
-
},
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
function defaultElementTransform(element) {
|
|
69
|
-
return { boundingClientRect: element.getBoundingClientRect() };
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const elementTagCategories = {
|
|
73
|
-
hasValue: [
|
|
74
|
-
"BUTTON",
|
|
75
|
-
"INPUT",
|
|
76
|
-
"OPTION",
|
|
77
|
-
"LI",
|
|
78
|
-
"METER",
|
|
79
|
-
"PROGRESS",
|
|
80
|
-
"PARAM",
|
|
81
|
-
"SELECT",
|
|
82
|
-
"TEXTAREA",
|
|
83
|
-
],
|
|
84
|
-
hasCurrentTime: ["AUDIO", "VIDEO"],
|
|
85
|
-
hasFiles: ["INPUT"],
|
|
86
|
-
hasElements: ["FORM"],
|
|
87
|
-
hasName: [
|
|
88
|
-
"BUTTON",
|
|
89
|
-
"FORM",
|
|
90
|
-
"FIELDSET",
|
|
91
|
-
"IFRAME",
|
|
92
|
-
"INPUT",
|
|
93
|
-
"KEYGEN",
|
|
94
|
-
"OBJECT",
|
|
95
|
-
"OUTPUT",
|
|
96
|
-
"SELECT",
|
|
97
|
-
"TEXTAREA",
|
|
98
|
-
"MAP",
|
|
99
|
-
"META",
|
|
100
|
-
"PARAM",
|
|
101
|
-
],
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const elementTransforms = {};
|
|
105
|
-
|
|
106
|
-
Object.keys(elementTagCategories).forEach((category) => {
|
|
107
|
-
elementTagCategories[category].forEach((type) => {
|
|
108
|
-
const transforms =
|
|
109
|
-
elementTransforms[type] || (elementTransforms[type] = []);
|
|
110
|
-
transforms.push(elementTransformCategories[category]);
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
function EventTransformCategories() {
|
|
115
|
-
this.clipboard = (event) => ({
|
|
116
|
-
clipboardData: event.clipboardData,
|
|
117
|
-
});
|
|
118
|
-
this.composition = (event) => ({
|
|
119
|
-
data: event.data,
|
|
120
|
-
});
|
|
121
|
-
this.keyboard = (event) => ({
|
|
122
|
-
altKey: event.altKey,
|
|
123
|
-
charCode: event.charCode,
|
|
124
|
-
ctrlKey: event.ctrlKey,
|
|
125
|
-
key: event.key,
|
|
126
|
-
keyCode: event.keyCode,
|
|
127
|
-
locale: event.locale,
|
|
128
|
-
location: event.location,
|
|
129
|
-
metaKey: event.metaKey,
|
|
130
|
-
repeat: event.repeat,
|
|
131
|
-
shiftKey: event.shiftKey,
|
|
132
|
-
which: event.which,
|
|
133
|
-
});
|
|
134
|
-
this.mouse = (event) => ({
|
|
135
|
-
altKey: event.altKey,
|
|
136
|
-
button: event.button,
|
|
137
|
-
buttons: event.buttons,
|
|
138
|
-
clientX: event.clientX,
|
|
139
|
-
clientY: event.clientY,
|
|
140
|
-
ctrlKey: event.ctrlKey,
|
|
141
|
-
metaKey: event.metaKey,
|
|
142
|
-
pageX: event.pageX,
|
|
143
|
-
pageY: event.pageY,
|
|
144
|
-
screenX: event.screenX,
|
|
145
|
-
screenY: event.screenY,
|
|
146
|
-
shiftKey: event.shiftKey,
|
|
147
|
-
});
|
|
148
|
-
this.pointer = (event) => ({
|
|
149
|
-
...this.mouse(event),
|
|
150
|
-
pointerId: event.pointerId,
|
|
151
|
-
width: event.width,
|
|
152
|
-
height: event.height,
|
|
153
|
-
pressure: event.pressure,
|
|
154
|
-
tiltX: event.tiltX,
|
|
155
|
-
tiltY: event.tiltY,
|
|
156
|
-
pointerType: event.pointerType,
|
|
157
|
-
isPrimary: event.isPrimary,
|
|
158
|
-
});
|
|
159
|
-
this.selection = () => {
|
|
160
|
-
return { selectedText: window.getSelection().toString() };
|
|
161
|
-
};
|
|
162
|
-
this.touch = (event) => ({
|
|
163
|
-
altKey: event.altKey,
|
|
164
|
-
ctrlKey: event.ctrlKey,
|
|
165
|
-
metaKey: event.metaKey,
|
|
166
|
-
shiftKey: event.shiftKey,
|
|
167
|
-
});
|
|
168
|
-
this.ui = (event) => ({
|
|
169
|
-
detail: event.detail,
|
|
170
|
-
});
|
|
171
|
-
this.wheel = (event) => ({
|
|
172
|
-
deltaMode: event.deltaMode,
|
|
173
|
-
deltaX: event.deltaX,
|
|
174
|
-
deltaY: event.deltaY,
|
|
175
|
-
deltaZ: event.deltaZ,
|
|
176
|
-
});
|
|
177
|
-
this.animation = (event) => ({
|
|
178
|
-
animationName: event.animationName,
|
|
179
|
-
pseudoElement: event.pseudoElement,
|
|
180
|
-
elapsedTime: event.elapsedTime,
|
|
181
|
-
});
|
|
182
|
-
this.transition = (event) => ({
|
|
183
|
-
propertyName: event.propertyName,
|
|
184
|
-
pseudoElement: event.pseudoElement,
|
|
185
|
-
elapsedTime: event.elapsedTime,
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const eventTypeCategories = {
|
|
190
|
-
clipboard: ["copy", "cut", "paste"],
|
|
191
|
-
composition: ["compositionend", "compositionstart", "compositionupdate"],
|
|
192
|
-
keyboard: ["keydown", "keypress", "keyup"],
|
|
193
|
-
mouse: [
|
|
194
|
-
"click",
|
|
195
|
-
"contextmenu",
|
|
196
|
-
"doubleclick",
|
|
197
|
-
"drag",
|
|
198
|
-
"dragend",
|
|
199
|
-
"dragenter",
|
|
200
|
-
"dragexit",
|
|
201
|
-
"dragleave",
|
|
202
|
-
"dragover",
|
|
203
|
-
"dragstart",
|
|
204
|
-
"drop",
|
|
205
|
-
"mousedown",
|
|
206
|
-
"mouseenter",
|
|
207
|
-
"mouseleave",
|
|
208
|
-
"mousemove",
|
|
209
|
-
"mouseout",
|
|
210
|
-
"mouseover",
|
|
211
|
-
"mouseup",
|
|
212
|
-
],
|
|
213
|
-
pointer: [
|
|
214
|
-
"pointerdown",
|
|
215
|
-
"pointermove",
|
|
216
|
-
"pointerup",
|
|
217
|
-
"pointercancel",
|
|
218
|
-
"gotpointercapture",
|
|
219
|
-
"lostpointercapture",
|
|
220
|
-
"pointerenter",
|
|
221
|
-
"pointerleave",
|
|
222
|
-
"pointerover",
|
|
223
|
-
"pointerout",
|
|
224
|
-
],
|
|
225
|
-
selection: ["select"],
|
|
226
|
-
touch: ["touchcancel", "touchend", "touchmove", "touchstart"],
|
|
227
|
-
ui: ["scroll"],
|
|
228
|
-
wheel: ["wheel"],
|
|
229
|
-
animation: ["animationstart", "animationend", "animationiteration"],
|
|
230
|
-
transition: ["transitionend"],
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
const eventTransforms = {};
|
|
234
|
-
|
|
235
|
-
const eventTransformCategories = new EventTransformCategories();
|
|
236
|
-
Object.keys(eventTypeCategories).forEach((category) => {
|
|
237
|
-
eventTypeCategories[category].forEach((type) => {
|
|
238
|
-
eventTransforms[type] = eventTransformCategories[category];
|
|
239
|
-
});
|
|
240
|
-
});
|
package/src/import-source.js
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
|
|
3
|
-
import { LayoutContext } from "./contexts.js";
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
createElementAttributes,
|
|
7
|
-
createElementChildren,
|
|
8
|
-
} from "./element-utils.js";
|
|
9
|
-
|
|
10
|
-
export function useImportSource(modelImportSource) {
|
|
11
|
-
const layoutContext = React.useContext(LayoutContext);
|
|
12
|
-
const [importSource, setImportSource] = React.useState(null);
|
|
13
|
-
|
|
14
|
-
React.useEffect(() => {
|
|
15
|
-
let unmounted = false;
|
|
16
|
-
|
|
17
|
-
loadModelImportSource(layoutContext, modelImportSource).then((src) => {
|
|
18
|
-
if (!unmounted) {
|
|
19
|
-
setImportSource(src);
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
return () => {
|
|
24
|
-
unmounted = true;
|
|
25
|
-
};
|
|
26
|
-
}, [layoutContext, modelImportSource, setImportSource]);
|
|
27
|
-
|
|
28
|
-
return importSource;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function loadModelImportSource(layoutContext, importSource) {
|
|
32
|
-
return layoutContext
|
|
33
|
-
.loadImportSource(importSource.source, importSource.sourceType)
|
|
34
|
-
.then((module) => {
|
|
35
|
-
if (typeof module.bind === "function") {
|
|
36
|
-
return {
|
|
37
|
-
data: importSource,
|
|
38
|
-
bind: (node) => {
|
|
39
|
-
const shortImportSource = {
|
|
40
|
-
source: importSource.source,
|
|
41
|
-
sourceType: importSource.sourceType,
|
|
42
|
-
};
|
|
43
|
-
const binding = module.bind(node, layoutContext);
|
|
44
|
-
if (
|
|
45
|
-
typeof binding.create === "function" &&
|
|
46
|
-
typeof binding.render === "function" &&
|
|
47
|
-
typeof binding.unmount === "function"
|
|
48
|
-
) {
|
|
49
|
-
return {
|
|
50
|
-
render: (model) =>
|
|
51
|
-
binding.render(
|
|
52
|
-
createElementFromModuleBinding(
|
|
53
|
-
layoutContext,
|
|
54
|
-
importSource,
|
|
55
|
-
module,
|
|
56
|
-
binding,
|
|
57
|
-
model
|
|
58
|
-
)
|
|
59
|
-
),
|
|
60
|
-
unmount: binding.unmount,
|
|
61
|
-
};
|
|
62
|
-
} else {
|
|
63
|
-
console.error(
|
|
64
|
-
`${importSource.source} returned an impropper binding`
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
};
|
|
69
|
-
} else {
|
|
70
|
-
console.error(
|
|
71
|
-
`${importSource.source} did not export a function 'bind'`
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function createElementFromModuleBinding(
|
|
78
|
-
layoutContext,
|
|
79
|
-
currentImportSource,
|
|
80
|
-
module,
|
|
81
|
-
binding,
|
|
82
|
-
model
|
|
83
|
-
) {
|
|
84
|
-
let type;
|
|
85
|
-
if (model.importSource) {
|
|
86
|
-
if (!isImportSourceEqual(currentImportSource, model.importSource)) {
|
|
87
|
-
console.error(
|
|
88
|
-
"Parent element import source " +
|
|
89
|
-
stringifyImportSource(currentImportSource) +
|
|
90
|
-
" does not match child's import source " +
|
|
91
|
-
stringifyImportSource(model.importSource)
|
|
92
|
-
);
|
|
93
|
-
return null;
|
|
94
|
-
} else if (!module[model.tagName]) {
|
|
95
|
-
console.error(
|
|
96
|
-
"Module from source " +
|
|
97
|
-
stringifyImportSource(currentImportSource) +
|
|
98
|
-
` does not export ${model.tagName}`
|
|
99
|
-
);
|
|
100
|
-
return null;
|
|
101
|
-
} else {
|
|
102
|
-
type = module[model.tagName];
|
|
103
|
-
}
|
|
104
|
-
} else {
|
|
105
|
-
type = model.tagName;
|
|
106
|
-
}
|
|
107
|
-
return binding.create(
|
|
108
|
-
type,
|
|
109
|
-
createElementAttributes(model, layoutContext.sendEvent),
|
|
110
|
-
createElementChildren(model, (child) =>
|
|
111
|
-
createElementFromModuleBinding(
|
|
112
|
-
layoutContext,
|
|
113
|
-
currentImportSource,
|
|
114
|
-
module,
|
|
115
|
-
binding,
|
|
116
|
-
child
|
|
117
|
-
)
|
|
118
|
-
)
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function isImportSourceEqual(source1, source2) {
|
|
123
|
-
return (
|
|
124
|
-
source1.source === source2.source &&
|
|
125
|
-
source1.sourceType === source2.sourceType
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function stringifyImportSource(importSource) {
|
|
130
|
-
return JSON.stringify({
|
|
131
|
-
source: importSource.source,
|
|
132
|
-
sourceType: importSource.sourceType,
|
|
133
|
-
});
|
|
134
|
-
}
|
package/src/index.js
DELETED