Skip to content

Commit

Permalink
interim support for plotly (#318)
Browse files Browse the repository at this point in the history
* 🪓 hack module loading

* ⏱ no need to pre-load

* 🪝converting to hooks
  • Loading branch information
stevejpurves authored Mar 5, 2024
1 parent 614a416 commit a04a70d
Show file tree
Hide file tree
Showing 7 changed files with 1,816 additions and 21 deletions.
1,733 changes: 1,721 additions & 12 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions packages/jupyter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@scienceicons/react": "^0.0.6",
"buffer": "^6.0.3",
"classnames": "^2.3.2",
"jupyterlab-plotly": "^5.18.0",
"myst-common": "^1.1.18",
"myst-config": "^1.1.18",
"myst-frontmatter": "^1.1.18",
Expand All @@ -49,6 +50,5 @@
"@types/react-dom": "^16.8 || ^17.0 || ^18.0",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"devDependencies": {}
}
}
8 changes: 6 additions & 2 deletions packages/jupyter/src/execute/leaf.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
selectNotebookForPage,
} from './selectors.js';
import { useFetchMdast } from 'myst-to-react';
import { useLoadPlotly } from '../plotly.js';

export function MdastFetcher({
slug,
Expand Down Expand Up @@ -58,11 +59,14 @@ export function NotebookBuilder({

const scopeHasNotebook = !!state.pages[pageSlug]?.scopes[notebookSlug];

const { plotly } = useLoadPlotly();

useEffect(() => {
if (!core || !config || scopeHasNotebook || lock.current) return;
if (!core || !config || !plotly || scopeHasNotebook || lock.current) return;
lock.current = true;
console.debug(`Jupyter: NotebookBuilder - ${notebookSlug} being added to scope ${pageSlug}`);
const rendermime = core?.makeRenderMimeRegistry(config?.mathjax);
if (plotly) rendermime.addFactory(plotly.rendererFactory, 41);
const notebook = notebookFromMdast(
core,
config,
Expand All @@ -86,7 +90,7 @@ export function NotebookBuilder({
type: 'ADD_NOTEBOOK',
payload: { pageSlug, notebookSlug, rendermime, notebook },
});
}, [core, config, pageSlug, notebookSlug, scopeHasNotebook, lock]);
}, [core, config, pageSlug, notebookSlug, scopeHasNotebook, lock, plotly]);

// TODO find a way to check if the all the notebooks are built and do a single dispatch
// potentilly use a move the loop down into this component
Expand Down
3 changes: 3 additions & 0 deletions packages/jupyter/src/globals.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare module 'jupyterlab-plotly/lib/plotly-renderer.js';
declare module 'jupyterlab-plotly/dist/index.js';
declare module 'plotly.js/dist/plotly';
16 changes: 11 additions & 5 deletions packages/jupyter/src/jupyter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import { useFetchAnyTruncatedContent } from './hooks.js';
import type { MinifiedOutput } from 'nbtx';
import { convertToIOutputs } from 'nbtx';
import { fetchAndEncodeOutputImages } from './convertImages.js';
import { type ThebeCore } from 'thebe-core';
import type { ThebeCore } from 'thebe-core';
import { SourceFileKind } from 'myst-spec-ext';
import { useXRefState } from '@myst-theme/providers';
import { useThebeLoader } from 'thebe-react';
import { useCellExecution } from './execute/index.js';
import { usePlaceholder } from './decoration.js';
import { MyST } from 'myst-to-react';
import classNames from 'classnames';
import { usePlotlyPassively } from './plotly.js';

function ActiveOutputRenderer({
id,
Expand Down Expand Up @@ -75,14 +76,19 @@ function PassiveOutputRenderer({
core: ThebeCore;
kind: SourceFileKind;
}) {
const cell = useRef(new core.PassiveCellRenderer(id, undefined, undefined));
const rendermime = core.makeRenderMimeRegistry();

const cell = useRef(new core.PassiveCellRenderer(id, rendermime, undefined));
const ref = useRef<HTMLDivElement>(null);

const { loaded } = usePlotlyPassively(rendermime, data);

useEffect(() => {
if (!ref.current) return;
cell.current.attachToDOM(ref.current, true);
if (!ref.current || !loaded) return;
// eslint-disable-next-line import/no-extraneous-dependencies
cell.current.attachToDOM(ref.current ?? undefined, true);
cell.current.render(core?.stripWidgets(data) ?? data);
}, [ref]);
}, [ref, loaded]);

return <div ref={ref} data-thebe-passive-ref="true" />;
}
Expand Down
52 changes: 52 additions & 0 deletions packages/jupyter/src/plotly.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useEffect, useState } from 'react';
import type { IRenderMime, IRenderMimeRegistry } from '@jupyterlab/rendermime';
import type { IOutput } from '@jupyterlab/nbformat';

export function useLoadPlotly() {
const [plotly, setPlotly] = useState<
{ rendererFactory: IRenderMime.IRendererFactory } | undefined
>();

useEffect(() => {
if (plotly) return;
// eslint-disable-next-line import/no-extraneous-dependencies
import('jupyterlab-plotly/lib/plotly-renderer.js').then(
(module: { rendererFactory: IRenderMime.IRendererFactory }) => {
console.debug('Jupyter: Adding plotly renderer factory to rendermime registry', {
module,
});
setPlotly(module);
},
);
}, [plotly]);

return { plotly };
}

const PLOTLY_MIMETYPE = 'application/vnd.plotly.v1+json';

export function isPlotly(outputs: IOutput[]) {
return outputs.some((output) => Object.keys(output.data ?? []).includes(PLOTLY_MIMETYPE));
}

export function usePlotlyPassively(rendermime: IRenderMimeRegistry, outputs: IOutput[]) {
const [loaded, setLoaded] = useState(false);

const isPlotlyOutput = isPlotly(outputs);

useEffect(() => {
if (loaded || !isPlotlyOutput) return;
// eslint-disable-next-line import/no-extraneous-dependencies
import('jupyterlab-plotly/lib/plotly-renderer.js').then(
(module: { rendererFactory: IRenderMime.IRendererFactory }) => {
console.debug('Jupyter: Adding plotly renderer factory to rendermime registry', {
module,
});
rendermime.addFactory(module.rendererFactory, 41);
setLoaded(true);
},
);
}, [loaded, isPlotlyOutput]);

return { loaded };
}
21 changes: 21 additions & 0 deletions patches/jupyterlab-plotly+5.18.0.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
diff --git a/node_modules/jupyterlab-plotly/lib/plotly-renderer.js b/node_modules/jupyterlab-plotly/lib/plotly-renderer.js
index 23baa49..43c857a 100644
--- a/node_modules/jupyterlab-plotly/lib/plotly-renderer.js
+++ b/node_modules/jupyterlab-plotly/lib/plotly-renderer.js
@@ -110,12 +110,15 @@ export class RenderedPlotly extends Widget {
const loadPlotly = () => __awaiter(this, void 0, void 0, function* () {
if (RenderedPlotly.Plotly === null) {
RenderedPlotly.Plotly = yield import("plotly.js/dist/plotly");
+ RenderedPlotly.Plotly = RenderedPlotly.Plotly.default;
RenderedPlotly._resolveLoadingPlotly();
}
return RenderedPlotly.loadingPlotly;
});
return loadPlotly()
- .then(() => RenderedPlotly.Plotly.react(this.node, data, layout, config))
+ .then(() => {
+ return RenderedPlotly.Plotly.react(this.node, data, layout, config)
+ })
.then((plot) => {
this.showGraph();
this.hideImage();

0 comments on commit a04a70d

Please sign in to comment.