Skip to content

Commit 74494ff

Browse files
committed
roll custom suspense solution
unfortunately the builtin Suspense has a habbit of flickering the fallback whenever the view updates, so we needed a custom solution to this
1 parent 73da176 commit 74494ff

File tree

2 files changed

+102
-93
lines changed

2 files changed

+102
-93
lines changed

idom/client/core_modules/layout.js

Lines changed: 102 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,16 @@ import ReactDOM from "../web_modules/react-dom.js";
88
import htm from "../web_modules/htm.js";
99

1010
import serializeEvent from "./event-to-object.js";
11-
import lazyComponent from "./lazy-component.js";
1211

1312
const html = htm.bind(React.createElement);
13+
const alreadyImported = {};
1414

15-
function Layout({ endpoint }) {
15+
export function renderLayout(mountElement, endpoint) {
16+
const cmpt = html`<${Layout} endpoint=${endpoint} />`;
17+
return ReactDOM.render(cmpt, mountElement);
18+
}
19+
20+
export default function Layout({ endpoint }) {
1621
// handle relative endpoint URI
1722
if (endpoint.startsWith(".") || endpoint.startsWith("/")) {
1823
let loc = window.location;
@@ -48,36 +53,48 @@ function Layout({ endpoint }) {
4853
body: { event: event },
4954
});
5055
};
51-
5256
if (modelState.root && modelState.models[modelState.root]) {
5357
return html`<${Element}
5458
modelState=${modelState}
55-
model=${modelState.models[modelState.root]}
5659
sendEvent=${sendEvent}
60+
model=${modelState.models[modelState.root]}
5761
/>`;
5862
} else {
5963
return html`<div />`;
6064
}
6165
}
6266

63-
function Element({ modelState, model, sendEvent }) {
64-
const children = elementChildren(modelState, model, sendEvent);
65-
const attributes = elementAttributes(model, sendEvent);
67+
function Element({ modelState, sendEvent, model }) {
6668
if (model.importSource) {
67-
const cmpt = lazyComponent(model);
68-
return html`
69-
<${Suspense} fallback="${model.importSource.fallback}">
70-
<${cmpt} ...${attributes}>${children}<//>
71-
<//>
72-
`;
73-
} else if (model.children && model.children.length) {
74-
return html`<${model.tagName} ...${attributes}>${children}<//>`;
69+
return html`<${LazyElement}
70+
modelState=${modelState}
71+
sendEvent=${sendEvent}
72+
model=${model}
73+
/>`;
74+
} else {
75+
const children = elementChildren(modelState, sendEvent, model);
76+
const attributes = elementAttributes(sendEvent, model);
77+
if (model.children && model.children.length) {
78+
return html`<${model.tagName} ...${attributes}>${children}<//>`;
79+
} else {
80+
return html`<${model.tagName} ...${attributes} />`;
81+
}
82+
}
83+
}
84+
85+
function LazyElement({ modelState, sendEvent, model }) {
86+
const module = useLazyModule(model.importSource.source);
87+
if (module) {
88+
const cmpt = getPathProperty(module, model.tagName);
89+
const children = elementChildren(modelState, sendEvent, model);
90+
const attributes = elementAttributes(sendEvent, model);
91+
return html`<${cmpt} ...${attributes}>${children}<//>`;
7592
} else {
76-
return html`<${model.tagName} ...${attributes} />`;
93+
return html`<div>${model.importSource.fallback}<//>`;
7794
}
7895
}
7996

80-
function elementChildren(modelState, model, sendEvent) {
97+
function elementChildren(modelState, sendEvent, model) {
8198
if (!model.children) {
8299
return [];
83100
} else {
@@ -106,45 +123,84 @@ function elementChildren(modelState, model, sendEvent) {
106123
}
107124
}
108125

109-
function elementAttributes(model, sendEvent) {
126+
function elementAttributes(sendEvent, model) {
110127
const attributes = Object.assign({}, model.attributes);
111128

112129
if (model.eventHandlers) {
113130
Object.keys(model.eventHandlers).forEach((eventName) => {
114131
const eventSpec = model.eventHandlers[eventName];
115-
attributes[eventName] = function eventHandler(event) {
116-
const data = Array.from(arguments).map((value) => {
117-
if (typeof value === "object" && value.nativeEvent) {
118-
if (eventSpec["preventDefault"]) {
119-
value.preventDefault();
120-
}
121-
if (eventSpec["stopPropagation"]) {
122-
value.stopPropagation();
123-
}
124-
return serializeEvent(value);
125-
} else {
126-
return value;
127-
}
128-
});
129-
const sentEvent = new Promise((resolve, reject) => {
130-
const msg = {
131-
data: data,
132-
target: eventSpec["target"],
133-
};
134-
sendEvent(msg);
135-
resolve(msg);
136-
});
137-
};
132+
attributes[eventName] = eventHandler(sendEvent, eventSpec);
138133
});
139134
}
140135

141136
return attributes;
142137
}
143138

144-
function renderLayout(mountElement, endpoint) {
145-
const cmpt = html`<${Layout} endpoint=${endpoint} />`;
146-
return ReactDOM.render(cmpt, mountElement);
139+
function eventHandler(sendEvent, eventSpec) {
140+
return function () {
141+
const data = Array.from(arguments).map((value) => {
142+
if (typeof value === "object" && value.nativeEvent) {
143+
if (eventSpec["preventDefault"]) {
144+
value.preventDefault();
145+
}
146+
if (eventSpec["stopPropagation"]) {
147+
value.stopPropagation();
148+
}
149+
return serializeEvent(value);
150+
} else {
151+
return value;
152+
}
153+
});
154+
const sentEvent = new Promise((resolve, reject) => {
155+
const msg = {
156+
data: data,
157+
target: eventSpec["target"],
158+
};
159+
sendEvent(msg);
160+
resolve(msg);
161+
});
162+
};
163+
}
164+
165+
function useLazyModule(source) {
166+
const [module, setModule] = useState(alreadyImported[source]);
167+
if (!module) {
168+
dynamicImport(source).then(setModule);
169+
}
170+
return module;
171+
}
172+
173+
function dynamicImport(source) {
174+
return eval(`import('${source}')`).then(
175+
(pkg) => (pkg.default ? pkg.default : pkg),
176+
(error) => {
177+
if (!error.stack) {
178+
throw error;
179+
} else {
180+
console.log(error);
181+
return {
182+
default: function Catch() {
183+
return html`
184+
<pre>
185+
<h1>Error</h1>
186+
<code>${[error.stack, error.message]}</code>
187+
</pre
188+
>
189+
`;
190+
},
191+
};
192+
}
193+
}
194+
);
147195
}
148196

149-
export default Layout;
150-
export { renderLayout };
197+
function getPathProperty(obj, prop) {
198+
// properties may be dot seperated strings
199+
const path = prop.split(".");
200+
const firstProp = path.shift();
201+
let value = obj[firstProp];
202+
for (let i = 0; i < path.length; i++) {
203+
value = value[path[i]];
204+
}
205+
return value;
206+
}

idom/client/core_modules/lazy-component.js

Lines changed: 0 additions & 47 deletions
This file was deleted.

0 commit comments

Comments
 (0)