phosphor
lets you build desktop-style applications, with DOM provided by your favorite libraries like d3
, react
, and friends. If you're using JupyterLab... you're already using Phosphor! For the most part, the average user won't ever have to touch Phosphor code. But this is Jyve. Use these unsafe, loaded footguns to interactively learn about Phosphor development!
phosphor
¶While there is no phosphor
package on npm
, we've gone ahead and loaded one in the global namespace if you have installed @deathbeds/jyve-lyb-phosphor
. Like Jyve, Phosphor is developed in a single repo, but distributed as different packages. Because we don't have a generalized import system figured out yet, we just make this one up for you. We're going to use algorithms
, messaging
and widgets
.
🤔 How might we make "standard lib" JupyterLab libraries easy and reliable to use in Jyve kernels?¶
W = phosphor.widgets
M = phosphor.messaging
A = phosphor.algorithm
[object Object]
Since we have phosphor
locally, let's use it to build a nice little documentation browser, attaching directly the JupyterLab application shell.
docs = new W.DockPanel()
docs.title.label = 'PhosphorJS'
docs.title.closable = true
docs.title.iconClass = 'jp-QuestionMarkIcon jp-MaterialIcon'
docs.id = 'phosphor-docs'
keys = Object.keys(phosphor).filter((m) => m !== 'CSS')
keys.sort()
keys.map((m) => {
let d = new W.Widget({node: document.createElement('iframe')})
d.title.label = m
d.title.iconClass = 'jyv-Lyb-Phosphor' // not strictly required, but pretty easy!
d.node.src = `https://phosphorjs.github.io/phosphor/api/${m}/globals.html`
docs.layout.addWidget(d, {mode: 'tab-before'});
})
JupyterLab.shell.addToMainArea(docs, {mode: 'split-bottom'})
That right there is probably a viable JupyterLab extension.
🤔 How might we make a Jyve Notebook into a JupyterLab extension?¶
Interacting with JupyterLab is great, and if you have Jyve, you have JupyterLab. However, you might want to craft a simpler experience, but still use phosphor. Without the JupyterLab style machinery, you'll need to do some work.
sha = 'cc4052e5fda7e6d8e4dc4a78c0b2cde38b3c0e11'
styleRoot = `https://cdn.rawgit.com/phosphorjs/phosphor/${sha}/examples/example-dockpanel/style`
coreStyle = document.createElement('style')
coreStyle.innerHTML = phosphor.CSS.join("\n")
demoStyle = document.createElement('style')
demoStyle.innerHTML = ['index']
.map((s) => `@import "${styleRoot}/${s}.css";`)
.join("\n")
localStyle = document.createElement('style')
localStyle.innerHTML = `
body {
display: flex;
flex-direction: column;
padding: 5px;
}
`
document.body.appendChild(coreStyle)
document.body.appendChild(demoStyle)
document.body.appendChild(localStyle)
[object HTMLStyleElement]
main = new W.DockPanel()
main.id = 'main' // there's nothing special about main, but it's used in the demo CSS
window.onresize = () => { main.update(); }
() => { main.update(); }
Widget.attach
¶phosphor
is loaded in the same context as JupyterLab, and is somewhat bound to the window
object that hosts your Lab. Other problems will ensue, but this hack handles the basic case of getting all of the messaging set up inside the Jyve iframe
.
🤔 How might we get a truly locally-hosted copy of
phosphor
in the iframe?¶
M.MessageLoop.sendMessage(main, W.Widget.Msg.BeforeAttach)
document.body.appendChild(main.node)
M.MessageLoop.sendMessage(main, W.Widget.Msg.AfterAttach)
greet = new W.Widget()
greet.id = 'greeter'
greet.addClass('content') // again, just because demo
greet.title.label = 'Hello'
greet.title.closable = true
main.layout.addWidget(greet)
Here we're using the native browser API, but one could use d3
, react
or any other approach you can make available.
h1 = document.createElement('h1')
h1.textContent = `Hello World`
greet.node.appendChild(h1)
[object HTMLHeadingElement]
Because you're connected to all the phosphor
machinery, you can seamlessly move DOM between a Jyve iframe and the main JupyterLab application.
btn = document.createElement('button')
btn.textContent = '⚠️ Do not press ⚠️'
btn.addEventListener('click', function(){
if(greet.parent === main){
JupyterLab.shell.addToMainArea(greet, {mode: 'split-right'})
} else {
main.layout.addWidget(greet)
}
})
greet.node.appendChild(btn)
[object HTMLButtonElement]