-
Notifications
You must be signed in to change notification settings - Fork 35
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
Add a top-level importSource attribute to VDOM components #100
Comments
I agree that it would be great to be able to use vdom to render React components in addition to native HTML elements. However, I prefer the following interface: from vdom import import_component
CanvasDraw = import_component('react-canvas-draw')
CanvasDraw(width='100%') I don't think that we want to encourage people to write Javascript in Python. The above approach allows people to reference Javascript from Python (in this case its referencing Additionally, the |
I have created an example notebook and a branch of jupyterlab (that patches @nteract/transform-vdom to support |
@gnestor I agree that def import_component(module):
return {'importSource': {'source': f"import('https://dev.jspm.io/{module})"}} IDOM profides a similar interface. You should be able to paste the following code into the IDOM editor and see the canvas: import idom
Canvas = idom.Import("react-canvas-draw")
Canvas() |
I agree that there's a need for more expressiveness than just providing to an npm module name. Allow me to clarify what I mean by "reference local Javascript served from the Jupyter server, e.g. You can create a JS file in JupyterLab (for example) that imports some npm modules and exports a higher-level component. I have an example in the binder above that looks like: // ReactPianoComponent.js
// Written using vanilla JS vs. JSX so that it can be imported by vdom without requiring transpilation
import React from '//dev.jspm.io/react';
import PianoDefault from '//dev.jspm.io/react-piano-component';
const { default: Piano } = PianoDefault;
function PianoContainer({ children }) {
return React.createElement(
'div',
{
className: 'interactive-piano__piano-container',
onMouseDown: event => event.preventDefault()
},
children
);
}
function AccidentalKey({ isPlaying, text, eventHandlers }) {
return React.createElement(
'div',
{ className: 'interactive-piano__accidental-key__wrapper' },
React.createElement(
'button',
{
className: `interactive-piano__accidental-key ${
isPlaying ? 'interactive-piano__accidental-key--playing' : ''
}`,
...eventHandlers
},
React.createElement('div', { className: 'interactive-piano__text' }, text)
)
);
}
function NaturalKey({ isPlaying, text, eventHandlers }) {
return React.createElement(
'button',
{
className: `interactive-piano__natural-key ${
isPlaying ? 'interactive-piano__natural-key--playing' : ''
}`,
...eventHandlers
},
React.createElement('div', { className: 'interactive-piano__text' }, text)
);
}
function PianoKey({
isNoteAccidental,
isNotePlaying,
startPlayingNote,
stopPlayingNote,
keyboardShortcuts
}) {
function handleMouseEnter(event) {
if (event.buttons) {
startPlayingNote();
}
}
const KeyComponent = isNoteAccidental ? AccidentalKey : NaturalKey;
const eventHandlers = {
onMouseDown: startPlayingNote,
onMouseEnter: handleMouseEnter,
onTouchStart: startPlayingNote,
onMouseUp: stopPlayingNote,
onMouseOut: stopPlayingNote,
onTouchEnd: stopPlayingNote
};
return React.createElement(KeyComponent, {
isPlaying: isNotePlaying,
text: keyboardShortcuts.join(' / '),
eventHandlers: eventHandlers
});
}
export default function InteractivePiano() {
return React.createElement(
PianoContainer,
null,
React.createElement(Piano, {
startNote: 'C4',
endNote: 'B5',
renderPianoKey: PianoKey,
keyboardMap: {
Q: 'C4',
2: 'C#4',
W: 'D4',
3: 'D#4',
E: 'E4',
R: 'F4',
5: 'F#4',
T: 'G4',
6: 'G#4',
Y: 'A4',
7: 'A#4',
U: 'B4',
V: 'C5',
G: 'C#5',
B: 'D5',
H: 'D#5',
N: 'E5',
M: 'F5',
K: 'F#5',
',': 'G5',
L: 'G#5',
'.': 'A5',
';': 'A#5',
'/': 'B5'
}
})
);
} This is an example of a React component that requires some callback props that you can't provide via vdom (e.g. Python callbacks), so we need to provide a higher-level component that provides these props in JS. Now from your notebook, we can write: Piano = import_component('./ReactPianoComponent')
Piano() This allows us to accomplish the same result as using |
Is there some way that we could still transpile JSX client-side? I just don't see anyone wanting to write vanilla JS for React. Even if you did, you'd probably end up re-writing it in JSX once you turned it into a real NPM package. |
Ya, vdom could transpile any files passed to I did a little searching and I think the solution to using JSX in pure ES6 is template literals: import { React, ReactDOM } from ‘//unpkg.com/es-react';
import htm from ‘//unpkg.com/htm'
const html = htm.bind(React.createElement)
class App extends React.Component {
render() {
return html`
<div>
App goes here
</div>
`;
}
}
ReactDOM.render(html`<${App} />`, document.body); |
@gnestor the template literals definitely are better. Perhaps transpiling could be optional? Maybe a field in |
Transpiling could be a later addition though. For now, I think we can suggest using |
Agreed 👍 |
This is implemented in IDOM now: https://idom.readthedocs.io/en/latest/javascript-modules.html |
Summary
I think it would be useful to include a top-level
importSource
attribute to the VDOM spec:The purpose of this attribute is to include ReactJS components in your VDOM that you write or import. You can see this demonstrated in the "ReactJS Components" example from my project
idom
:https://idom-sandbox.herokuapp.com/client/index.html
How it Should Work
The source string should be JSX that, when eval'd, will return a component. The
attributes
andchildren
from the rest of the VDOM will then be passed to this component. For example, the following model:should result in the the HTML below:
Importing Other Libraries
You should also be able to return a promise from the JSX that results in a component (hence the presence of the
fallback
key in the proposedimportSource
dict):Things to Think About
What if the JSX were like a module, and the
tagName
referred to a member of the exported object (this is similar to what I'm doing in iDOM right now):The idea behind this approach is that you could create an API for JSX that looks a bit like this:
The text was updated successfully, but these errors were encountered: