Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: basic hooks support #83

Merged
merged 8 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/web/components/ComponentTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ export default function ComponentTree({
showMonitor,
}: ComponentTreeParams) {
return (
<div className="App">
<div className={`App ${isDebug ? 'bwe-debug' : ''}`}>
<>
{showMonitor && (
<ComponentMonitor
metrics={metrics}
components={Object.values(components)}
/>
)}
<div id={getAppDomId(rootComponentPath)} className="iframe">
<div id={getAppDomId(rootComponentPath)} className="container-child">
{isDebug && '[root component placeholder]'}
</div>
<div className="iframes">
Expand Down
20 changes: 7 additions & 13 deletions apps/web/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -162,24 +162,18 @@
font-weight: 700;
}

.iframe {
flex: 1;
justify-content: space-between;
margin: 4px;
padding: 8px;
border: 1px solid blue;
}

.iframe > div {
min-width: 60px;
min-height: 60px;
}

span {
font-size: 12px;
}

.bwe-debug .container-child {
border: 1px solid blue;
padding: 0.5em;
margin-bottom: 1em;
}

.dom-label {
font-size: 0.7em;
font-family: 'Roboto Mono for Powerline';
text-align: center;
}
7 changes: 3 additions & 4 deletions packages/application/src/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,26 +86,25 @@ export function onRender({
children: [
...(isDebug
? [
React.createElement('span', { className: 'dom-label' }, [
React.createElement('div', { className: 'dom-label' }, [
'[',
React.createElement(
'a',
{
href: `/${componentPath}?isDebug=${isDebug}&showMonitor=${showMonitor}`,
},
componentPath
componentPath.split('/')[2]
),
`(${getComponentRenderCount(componentId)})]`,
]),
React.createElement('br'),
]
: []),
...(Array.isArray(componentChildren)
? componentChildren
: [componentChildren]),
],
id: componentId,
props: isDebug ? { ...props, className: 'iframe' } : props,
props,
type: node.type,
onMessageSent,
});
Expand Down
7 changes: 2 additions & 5 deletions packages/application/src/monitor.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import type { EventType, MessagePayload } from '@bos-web-engine/container';

import type {
ComponentInstance,
ComponentMetrics,
} from './types';
import type { ComponentInstance, ComponentMetrics } from './types';
import { BWEMessage } from './types';

interface ComponentId {
Expand Down Expand Up @@ -139,7 +136,7 @@ export function ComponentMonitor({
switch (message.type) {
case 'component.render': {
const { type, props } = message.node;
const formattedChildren = message.childComponents.length
const formattedChildren = message.childComponents?.length
? `with children ${message.childComponents
.map(({ componentId }) =>
formatComponentId(parseComponentId(componentId))
Expand Down
6 changes: 5 additions & 1 deletion packages/application/src/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import type {
ComponentDOMElement,
} from './types';

function isChildrenAllowed(elementType: string) {
return !(elementType in ['img']);
}

export function createElement({
children,
id,
Expand All @@ -17,7 +21,7 @@ export function createElement({
return React.createElement(
type,
deserializeProps({ id, props, onMessageSent }),
children
isChildrenAllowed(type) ? children : undefined
);
}

Expand Down
4 changes: 4 additions & 0 deletions packages/container/src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import type {
* @param deserializeProps Function to deserialize props passed on the event
* @param invokeCallback Function to execute the specified function in the current context
* @param invokeComponentCallback Function to execute the specified function, either in the current context or another Component's
* @param parentContainerId ID of the parent container
* @param postCallbackInvocationMessage Request invocation on external Component via window.postMessage
* @param postCallbackResponseMessage Send callback execution result to calling Component via window.postMessage
* @param preactRootComponentName Name of the Preact Fragment Component function (i.e. the root Component's name)
* @param renderDom Callback for rendering DOM within the component
* @param renderComponent Callback for rendering the Component
* @param requests The set of inter-Component callback requests being tracked by the Component
Expand All @@ -33,6 +35,7 @@ export function buildEventHandler({
parentContainerId,
postCallbackInvocationMessage,
postCallbackResponseMessage,
preactRootComponentName,
renderDom,
renderComponent,
requests,
Expand Down Expand Up @@ -120,6 +123,7 @@ export function buildEventHandler({
callbacks,
parentId: method,
childComponents: [],
preactRootComponentName,
})
);

Expand Down
38 changes: 36 additions & 2 deletions packages/container/src/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,23 @@ export function decodeJsonString(value: string) {
return value.toString().replace(/⁣/g, '\n').replace(/⁤/g, '\t');
}

/**
* Serialize props of a child Component to be rendered in the outer application
* NB there is a circular dependency between this function and `serializeNode`
* due to the fact that a rendered Component may be passed as props to a child
* Component.
* @param builtinComponents Set of builtin BOS Web Engine Components
* @param callbacks Component container's callbacks
* @param parentId Component's parent container
* @param preactRootComponentName The name of the root/Fragment Preact function
* @param props The props for this container's Component
* @param componentId The target Component ID
*/
export function serializeProps({
builtinComponents,
callbacks,
parentId,
preactRootComponentName,
props,
componentId,
}: SerializePropsParams): Props {
Expand All @@ -51,6 +64,7 @@ export function serializeProps({
childComponents: [],
node: value,
parentId,
preactRootComponentName,
});
} else if (typeof value === 'string') {
serializedValue = decodeJsonString(serializedValue);
Expand Down Expand Up @@ -194,12 +208,22 @@ interface BuildComponentIdParams {
parentComponentId: string;
}

/**
* Given a Preact node, build its Component tree and serialize for transmission
* @param builtinComponents Set of builtin BOS Web Engine Components
* @param callbacks Component container's callbacks
* @param childComponents Set of descendant Components accumulated across recursive invocations
* @param node The Preact Component to serialize
* @param parentId Component's parent container
* @param preactRootComponentName The name of the root/Fragment Preact function
*/
export function serializeNode({
builtinComponents,
node,
childComponents,
callbacks,
parentId,
preactRootComponentName,
}: SerializeNodeParams): SerializedNode {
function buildComponentId({
instanceId,
Expand Down Expand Up @@ -241,7 +265,7 @@ export function serializeNode({

if (typeof type === 'function') {
const { name: component } = type;
if (component === '_') {
if (component === preactRootComponentName) {
serializedElementType = 'div';
// @ts-expect-error
} else if (builtinComponents[component]) {
Expand Down Expand Up @@ -270,6 +294,7 @@ export function serializeNode({
builtinComponents,
parentId,
componentId,
preactRootComponentName,
})
: {},
source: src,
Expand All @@ -289,6 +314,7 @@ export function serializeNode({
__bweMeta: {
componentId: componentId,
},
className: 'container-child',
},
};
} else {
Expand All @@ -313,14 +339,21 @@ export function serializeNode({
parentId: componentId,
callbacks,
childComponents,
preactRootComponentName,
});
}
}

return {
type: serializedElementType,
props: {
...serializeProps({ props, builtinComponents, callbacks, parentId }),
...serializeProps({
props,
builtinComponents,
callbacks,
parentId,
preactRootComponentName,
}),
children: unifiedChildren.flat().map((c) =>
c?.props
? serializeNode({
Expand All @@ -329,6 +362,7 @@ export function serializeNode({
childComponents,
callbacks,
parentId,
preactRootComponentName,
})
: c
),
Expand Down
5 changes: 5 additions & 0 deletions packages/container/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ export interface ProcessEventParams {
parentContainerId: string | null;
postCallbackInvocationMessage: PostMessageComponentInvocationCallback;
postCallbackResponseMessage: PostMessageComponentResponseCallback;
preactRootComponentName: string;
props: any;
renderDom: (node: any) => object;
renderComponent: () => void;
Expand Down Expand Up @@ -252,13 +253,15 @@ export interface SerializeNodeParams {
childComponents: ComponentChildMetadata[];
callbacks: CallbackMap;
parentId: string;
preactRootComponentName: string;
}
export type SerializeNodeCallback = (
args: SerializeNodeParams
) => SerializedNode;

export interface SerializedNode {
childComponents?: ComponentChildMetadata[];
className?: string;
type: string;
props: NodeProps | ComponentProps;
}
Expand All @@ -273,6 +276,7 @@ export interface SerializePropsParams {
builtinComponents: BuiltinComponents;
callbacks: CallbackMap;
parentId: string;
preactRootComponentName: string;
props: any;
componentId?: string;
}
Expand All @@ -285,6 +289,7 @@ export interface SerializedComponentCallback {
export interface ComponentProps {
__bweMeta?: WebEngineMeta;
children?: any[];
className?: string;
id: string;
}

Expand Down
Loading