diff --git a/docs/source/javascript-components.rst b/docs/source/javascript-components.rst index 8a6539691..a8e4cfd0b 100644 --- a/docs/source/javascript-components.rst +++ b/docs/source/javascript-components.rst @@ -73,7 +73,12 @@ adheres to the following interface: loadImportSource(source: string, sourceType: "NAME" | "URL") => Module; } - type bind = (node: HTMLElement, context: LayoutContext) => ({ + type SourceInfo = { + source: string; + sourceType: string; + } + + type bind = (node: HTMLElement, context: LayoutContext, source: SourceInfo) => ({ render(component: any, props: Object, childModels: Array): void; unmount(): void; }); diff --git a/src/client/packages/idom-client-react/package.json b/src/client/packages/idom-client-react/package.json index ed86c10b1..45848f6ee 100644 --- a/src/client/packages/idom-client-react/package.json +++ b/src/client/packages/idom-client-react/package.json @@ -1,7 +1,7 @@ { "name": "idom-client-react", "description": "A client for IDOM implemented in React", - "version": "0.9.2", + "version": "0.10.0", "author": "Ryan Morshead", "license": "MIT", "type": "module", diff --git a/src/client/packages/idom-client-react/src/component.js b/src/client/packages/idom-client-react/src/component.js index ffc0712bb..594afa199 100644 --- a/src/client/packages/idom-client-react/src/component.js +++ b/src/client/packages/idom-client-react/src/component.js @@ -36,6 +36,33 @@ export function Element({ model }) { } } +export function elementChildren(modelChildren) { + if (!modelChildren) { + return []; + } else { + return modelChildren.map((child) => { + switch (typeof child) { + case "object": + return html`<${Element} key=${child.key} model=${child} />`; + case "string": + return child; + } + }); + } +} + +export function elementAttributes(model, sendEvent) { + const attributes = Object.assign({}, model.attributes); + + if (model.eventHandlers) { + for (const [eventName, eventSpec] of Object.entries(model.eventHandlers)) { + attributes[eventName] = eventHandler(sendEvent, eventSpec); + } + } + + return attributes; +} + function StandardElement({ model }) { const config = React.useContext(LayoutConfigContext); const children = elementChildren(model.children); @@ -95,33 +122,6 @@ function RenderImportedElement({ model, importSource }) { return html`
`; } -export function elementChildren(modelChildren) { - if (!modelChildren) { - return []; - } else { - return modelChildren.map((child) => { - switch (typeof child) { - case "object": - return html`<${Element} key=${child.key} model=${child} />`; - case "string": - return child; - } - }); - } -} - -export function elementAttributes(model, sendEvent) { - const attributes = Object.assign({}, model.attributes); - - if (model.eventHandlers) { - for (const [eventName, eventSpec] of Object.entries(model.eventHandlers)) { - attributes[eventName] = eventHandler(sendEvent, eventSpec); - } - } - - return attributes; -} - function eventHandler(sendEvent, eventSpec) { return function () { const data = Array.from(arguments).map((value) => { @@ -152,7 +152,11 @@ function loadImportSource(config, importSource) { return { data: importSource, bind: (node) => { - const binding = module.bind(node, config); + const shortImportSource = { + source: importSource.source, + sourceType: importSource.sourceType, + }; + const binding = module.bind(node, config, shortImportSource); if ( typeof binding.render == "function" && typeof binding.unmount == "function" diff --git a/src/idom/web/templates/react.js b/src/idom/web/templates/react.js index 6bbc66f73..0ea54fc31 100644 --- a/src/idom/web/templates/react.js +++ b/src/idom/web/templates/react.js @@ -10,23 +10,30 @@ import { elementChildren, } from "$CDN/idom-client-react"; -export function bind(node, config) { +export function bind(node, config, sourceInfo) { return { render: (component, props, children) => - ReactDOM.render(createElement(config, component, props, children), node), + ReactDOM.render( + createElement(config, sourceInfo, component, props, children), + node + ), unmount: () => ReactDOM.unmountComponentAtNode(node), }; } -function createElement(config, component, props, children) { +function createElement(config, sourceInfo, component, props, children) { return React.createElement( LayoutConfigContext.Provider, { value: config }, - React.createElement(component, props, ...createChildren(children, config)) + React.createElement( + component, + props, + ...createChildren(config, sourceInfo, children) + ) ); } -function createChildren(children, config) { +function createChildren(config, sourceInfo, children) { if (!children) { return []; } @@ -34,35 +41,40 @@ function createChildren(children, config) { if (typeof child == "string") { return child; } else if (child.importSource) { - return createElementFromThisImportSource(child, config); + return createElementFromThisImportSource(config, sourceInfo, child); } else { return React.createElement(Element, { model: child }); } }); } -function createElementFromThisImportSource(model, config) { - const Component = ThisImportSource[model.tagName]; - if (!Component) { +function createElementFromThisImportSource(config, sourceInfo, model) { + if ( + model.importSource.source != sourceInfo.source || + model.importSource.sourceType != sourceInfo.sourceType + ) { console.error( `Cannot create ${model.tagName} from different import source ` + `${model.importSource.source} (type: ${model.importSource.sourceType})` ); + return React.createElement("pre", {}, "error"); } return React.createElement( - Component, + ThisImportSource[model.tagName], elementAttributes(model, (event) => { - event.data = event.data.filter(value => { + event.data = event.data.filter((value) => { try { JSON.stringify(value); } catch (err) { - console.error(`Failed to serialize some event data for ${model.tagName}`) + console.error( + `Failed to serialize some event data for ${model.tagName}` + ); return false; } return true; - }) + }); config.sendEvent(event); }), - ...createChildren(model.children, config) + ...createChildren(config, sourceInfo, model.children) ); }