Last active
February 27, 2025 23:57
-
-
Save drcmda/cbc77a30681bf41791ccc29ee0f5d855 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Reconciler from 'react-reconciler' | |
import omit from 'lodash/omit' | |
import capitalize from 'lodash/capitalize' | |
import { actions as elementActions } from './store/elements' | |
import * as Elements from './elements' | |
const roots = new Map() | |
const emptyObject = {} | |
const Renderer = Reconciler({ | |
useSyncScheduling: true, | |
now: () => performance.now(), | |
// Create a new instance of whatever you need to create in your target system | |
// Here you set up initial props and event handlers | |
createInstance(type, props, rootContainerInstance, hostContext, internalInstanceHandle) { | |
const instance = new Elements[(capitalize(type))](rootContainerInstance, {}) | |
applyProps(instance, props, {}) | |
return instance | |
}, | |
// Append a first child | |
appendInitialChild(parentInstance, child) { | |
parentInstance.children = [...parentInstance.children, child.id] | |
}, | |
finalizeInitialChildren(instance, type, props, rootContainerInstance) { | |
return false | |
}, | |
prepareUpdate(instance, type, oldProps, newProps, rootContainerInstance, hostContext) { | |
return emptyObject | |
}, | |
getRootHostContext(rootContainerInstance) { | |
return emptyObject | |
}, | |
shouldDeprioritizeSubtree(type, props) { | |
return false | |
}, | |
getChildHostContext(parentHostContext, type) { | |
return emptyObject | |
}, | |
getPublicInstance(instance) { | |
return instance | |
}, | |
prepareForCommit() {}, | |
resetAfterCommit() {}, | |
shouldSetTextContent(props) { | |
return false | |
}, | |
mutation: { | |
// This is where you add the root element into the target container | |
appendChildToContainer(container, child) { | |
container.addElement(child) | |
}, | |
// From there on it's just basic adding, removing ... | |
appendChild(parentInstance, child) { | |
parentInstance.children = [...parentInstance.children, child.id] | |
}, | |
insertBefore(parentInstance, child, beforeChild) { | |
const index = parentInstance.children.indexOf(beforeChild.id) | |
parentInstance.children = [ | |
...parentInstance.children.slice(0, index), | |
child.id, | |
...parentInstance.children.slice(index), | |
] | |
}, | |
removeChild(parentInstance, child) { | |
parentInstance.children = parentInstance.children.filter(id => id !== child.id) | |
child.destroy() | |
child.unsubscribes = undefined | |
}, | |
removeChildFromContainer(parentInstance, child) { | |
parentInstance.removeElement(child.id) | |
child.destroy() | |
child.unsubscribes = undefined | |
}, | |
// This one updates prop changes | |
commitUpdate(instance, updatePayload, type, oldProps, newProps) { | |
applyProps(instance, newProps, oldProps) | |
}, | |
}, | |
}) | |
export default { | |
render(element, container) { | |
let root = roots.get(container) | |
if (!root) { | |
root = Renderer.createContainer(container) | |
roots.set(container, root) | |
} | |
Renderer.updateContainer(element, root, null, undefined) | |
return Renderer.getPublicRootInstance(root) | |
}, | |
unmountComponentAtNode(container) { | |
const root = roots.get(container) | |
if (root) Renderer.updateContainer(null, root, null, () => roots.delete(container)) | |
}, | |
} | |
// Internal stuff, that's how our particular target handles props and events | |
function applyProps(instance, newProps, oldProps) { | |
// Filter equals, events and reserved props | |
const sameProps = Object.keys(newProps).filter(key => newProps[key] === oldProps[key]) | |
const handlers = Object.keys(newProps).filter(key => typeof newProps[key] === 'function' && key.startsWith('on')) | |
const filteredProps = omit(newProps, [...sameProps, ...handlers, 'children', 'key', 'ref']) | |
if (Object.keys(filteredProps).length > 0) { | |
// Set props | |
instance.session.dispatch(elementActions.update(instance.id, filteredProps)) | |
// Set events | |
instance.unsubscribes = handlers.reduce((acc, key) => { | |
const name = key.charAt(2).toLowerCase() + key.substr(3) | |
// Remove old events and return new unsubscribe | |
if (instance.unsubscribes && instance.unsubscribes[key]) | |
instance.removeSubscription(instance.unsubscribes[key]) | |
return { ...acc, [key]: instance.observe(state => state[name], (state, old) => newProps[key](state, old)) } | |
}, {}) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react' | |
import { Provider, connect } from 'react-redux' | |
import PropTypes from 'prop-types' | |
import { actions } from './store/globals' | |
import ElementRenderer from './reconciler' | |
// Regular react-component that renders custom elements | |
@connect(({ globals }) => ({ camera: globals.camera, day: globals.day }), { | |
setCamera: actions.setCamera, | |
setColorMode: actions.setColorMode, | |
}) | |
class Root extends React.Component { | |
setCamera = () => this.props.setCamera(this.props.camera === 'orthographic' ? 'perspective' : 'orthographic') | |
setColorMode = () => this.props.setColorMode(!this.props.day) | |
render() { | |
return ( | |
<group format="Table"> | |
<checkbox name="Orthographic" value={this.props.camera === 'orthographic'} onValue={this.setCamera} /> | |
<checkbox name="Daylight" value={this.props.day} onValue={this.setColorMode} /> | |
</group> | |
) | |
} | |
} | |
const plugin = { /* This would be the target container/factory/canvas/document that receives the result */ } | |
const store = { /* application store, i.e. the result of redux.createStore */ } | |
ElementRenderer.render( | |
<Provider store={store}> | |
<Root /> | |
</Provider>, | |
plugin, | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment