@@ -6,9 +6,13 @@ import * as jsonpatch from "fast-json-patch";
66import serializeEvent from "./event-to-object";
77
88const html = htm.bind(react.createElement);
9- const alreadyImported = {} ;
9+ const LayoutConfigContext = react.createContext({}) ;
1010
11- export function mountLayoutWithWebSocket(mountElement, endpoint) {
11+ export function mountLayoutWithWebSocket(
12+ mountElement,
13+ endpoint,
14+ importSourceUrl
15+ ) {
1216 if (endpoint.startsWith(".") || endpoint.startsWith("/")) {
1317 let loc = window.location;
1418 let protocol;
@@ -42,70 +46,116 @@ export function mountLayoutWithWebSocket(mountElement, endpoint) {
4246 );
4347 }
4448
45- return mountLayout(mountElement, saveUpdateHook, sendCallback);
49+ return mountLayout(
50+ mountElement,
51+ saveUpdateHook,
52+ sendCallback,
53+ importSourceUrl
54+ );
4655}
4756
48- export function mountLayout(mountElement, saveUpdateHook, sendEvent) {
57+ export function mountLayout(
58+ mountElement,
59+ saveUpdateHook,
60+ sendEvent,
61+ importSourceUrl
62+ ) {
4963 reactDOM.render(
50- html`<${Layout} saveUpdateHook=${saveUpdateHook} sendEvent=${sendEvent} />`,
64+ html`
65+ <${Layout}
66+ saveUpdateHook=${saveUpdateHook}
67+ sendEvent=${sendEvent}
68+ importSourceUrl=${importSourceUrl}
69+ />
70+ `,
5171 mountElement
5272 );
5373}
5474
55- export default function Layout({ saveUpdateHook, sendEvent }) {
75+ export default function Layout({ saveUpdateHook, sendEvent, importSourceUrl }) {
5676 const [model, patchModel] = useInplaceJsonPatch({});
5777
5878 react.useEffect(() => saveUpdateHook(patchModel), [patchModel]);
5979
6080 if (model.tagName) {
61- return html`<${Element} sendEvent=${sendEvent} model=${model} />`;
81+ return html`
82+ <${LayoutConfigContext.Provider}
83+ value=${{
84+ sendEvent: sendEvent,
85+ importSourceUrl: importSourceUrl,
86+ }}
87+ >
88+ <${Element} model=${model} />
89+ <//>
90+ `;
6291 } else {
6392 return html`<div />`;
6493 }
6594}
6695
67- function Element({ sendEvent, model }) {
96+ function Element({ model }) {
6897 if (model.importSource) {
69- return html`<${LazyElement} sendEvent=${sendEvent } model=${model} />`;
98+ return html`<${ImportedElement } model=${model} />`;
7099 } else {
71- const children = elementChildren(sendEvent, model);
72- const attributes = elementAttributes(sendEvent, model);
73- if (model.children && model.children.length) {
74- return html`<${model.tagName} ...${attributes}>${children}<//>`;
75- } else {
76- return html`<${model.tagName} ...${attributes} />`;
77- }
100+ return html`<${StandardElement} model=${model} />`;
78101 }
79102}
80103
81- function LazyElement({ sendEvent, model }) {
82- const module = useLazyModule(model.importSource.source);
104+ function ImportedElement({ model }) {
105+ const config = react.useContext(LayoutConfigContext);
106+ const module = useLazyModule(
107+ model.importSource.source,
108+ config.importSourceUrl
109+ );
83110 if (module) {
84111 const cmpt = getPathProperty(module, model.tagName);
85- const children = elementChildren(sendEvent, model);
86- const attributes = elementAttributes(sendEvent, model );
112+ const children = elementChildren(model);
113+ const attributes = elementAttributes(model, config.sendEvent );
87114 return html`<${cmpt} ...${attributes}>${children}<//>`;
88115 } else {
89- return html`<div>${model.importSource.fallback}<//>`;
116+ return createElement(model.importSource.fallback);
117+ }
118+ }
119+
120+ function StandardElement({ model }) {
121+ const config = react.useContext(LayoutConfigContext);
122+ const children = elementChildren(model);
123+ const attributes = elementAttributes(model, config.sendEvent);
124+ if (model.children && model.children.length) {
125+ return html`<${model.tagName} ...${attributes}>${children}<//>`;
126+ } else {
127+ return html`<${model.tagName} ...${attributes} />`;
128+ }
129+ }
130+
131+ function createElement(value) {
132+ if (!value) {
133+ return html`<div />`;
134+ }
135+ switch (typeof value) {
136+ case "object":
137+ return html`<${Element} model=${value} />`;
138+ case "string":
139+ return html`<div>${value}</div>`;
90140 }
91141}
92142
93- function elementChildren(sendEvent, model) {
143+ function elementChildren(model) {
94144 if (!model.children) {
95145 return [];
96146 } else {
97147 return model.children.map((child) => {
98148 switch (typeof child) {
99149 case "object":
100- return html`<${Element} model=${child} sendEvent=${sendEvent} />`;
150+ return html`<${Element} model=${child} />`;
101151 case "string":
102152 return child;
103153 }
104154 });
105155 }
106156}
107157
108- function elementAttributes(sendEvent, model ) {
158+ function elementAttributes(model, sendEvent ) {
109159 const attributes = Object.assign({}, model.attributes);
110160
111161 if (model.eventHandlers) {
@@ -144,10 +194,12 @@ function eventHandler(sendEvent, eventSpec) {
144194 };
145195}
146196
147- function useLazyModule(source) {
148- const [module, setModule] = react.useState(alreadyImported[source] );
197+ function useLazyModule(source, sourceUrl = "" ) {
198+ const [module, setModule] = react.useState(null );
149199 if (!module) {
150- dynamicImport(source).then(setModule);
200+ dynamicImport(
201+ source.startsWith("./") ? sourceUrl + source.slice(2) : source
202+ ).then(setModule);
151203 }
152204 return module;
153205}
@@ -161,7 +213,7 @@ function dynamicImport(source) {
161213 } else {
162214 console.log(error);
163215 return {
164- default: function Catch () {
216+ default() {
165217 return html`
166218 <pre>
167219 <h1>Error</h1>
0 commit comments