mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-04-08 10:52:15 +01:00
* Initial commit * Optimizations * Optimize notebookOutputViewer bundle size by lazy loading transforms * Update package-lock.json * More optimizations * Updates * Fix unit test and other updates * Address feedback * Update package-lock.json * Update test snapshots * Fix build * Reduce cellOutputViewer bundle size * Renaming
133 lines
4.0 KiB
TypeScript
133 lines
4.0 KiB
TypeScript
import { JSONObject } from "@nteract/commutable";
|
|
import { outputToJS } from "@nteract/commutable/lib/v4";
|
|
import { actions, AppState, ContentRef, selectors } from "@nteract/core";
|
|
import IframeResizer from "iframe-resizer-react";
|
|
import Immutable from "immutable";
|
|
import postRobot from "post-robot";
|
|
import React from "react";
|
|
import { connect } from "react-redux";
|
|
import { Dispatch } from "redux";
|
|
import { CellOutputViewerProps } from "../../../../CellOutputViewer/CellOutputViewer";
|
|
|
|
// Adapted from https://github.com/nteract/nteract/blob/main/packages/stateful-components/src/outputs/index.tsx
|
|
// to add support for sandboxing using <iframe>
|
|
|
|
interface ComponentProps {
|
|
id: string;
|
|
contentRef: ContentRef;
|
|
}
|
|
|
|
interface StateProps {
|
|
hidden: boolean;
|
|
expanded: boolean;
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
outputs: Immutable.List<any>;
|
|
}
|
|
|
|
interface DispatchProps {
|
|
onMetadataChange?: (metadata: JSONObject, mediaType: string, index?: number) => void;
|
|
}
|
|
|
|
export class SandboxOutputs extends React.PureComponent<ComponentProps & StateProps & DispatchProps> {
|
|
private childWindow: Window;
|
|
|
|
render(): JSX.Element {
|
|
// Using min-width to set the width of the iFrame, works around an issue in iOS that can prevent the iFrame from sizing correctly.
|
|
return (
|
|
<IframeResizer
|
|
checkOrigin={false}
|
|
loading="lazy"
|
|
heightCalculationMethod="taggedElement"
|
|
onLoad={(event) => this.handleFrameLoad(event)}
|
|
src="./cellOutputViewer.html"
|
|
style={{ height: "1px", width: "1px", minWidth: "100%", border: "none" }}
|
|
sandbox="allow-downloads allow-popups allow-forms allow-pointer-lock allow-scripts allow-popups-to-escape-sandbox"
|
|
/>
|
|
);
|
|
}
|
|
|
|
handleFrameLoad(event: React.SyntheticEvent<HTMLIFrameElement, Event>): void {
|
|
this.childWindow = (event.target as HTMLIFrameElement).contentWindow;
|
|
this.sendPropsToFrame();
|
|
}
|
|
|
|
sendPropsToFrame(): void {
|
|
if (!this.childWindow) {
|
|
return;
|
|
}
|
|
|
|
const props: CellOutputViewerProps = {
|
|
id: this.props.id,
|
|
contentRef: this.props.contentRef,
|
|
hidden: this.props.hidden,
|
|
expanded: this.props.expanded,
|
|
outputs: this.props.outputs.toArray().map((output) => outputToJS(output)),
|
|
onMetadataChange: this.props.onMetadataChange,
|
|
};
|
|
|
|
postRobot.send(this.childWindow, "props", props);
|
|
}
|
|
|
|
componentDidMount(): void {
|
|
this.sendPropsToFrame();
|
|
}
|
|
|
|
componentDidUpdate(): void {
|
|
this.sendPropsToFrame();
|
|
}
|
|
}
|
|
|
|
export const makeMapStateToProps = (
|
|
initialState: AppState,
|
|
ownProps: ComponentProps
|
|
): ((state: AppState) => StateProps) => {
|
|
const mapStateToProps = (state: AppState): StateProps => {
|
|
let outputs = Immutable.List();
|
|
let hidden = false;
|
|
let expanded = false;
|
|
|
|
const { contentRef, id } = ownProps;
|
|
const model = selectors.model(state, { contentRef });
|
|
|
|
if (model && model.type === "notebook") {
|
|
const cell = selectors.notebook.cellById(model, { id });
|
|
if (cell) {
|
|
outputs = cell.get("outputs", Immutable.List());
|
|
hidden = cell.cell_type === "code" && cell.getIn(["metadata", "jupyter", "outputs_hidden"]);
|
|
expanded = cell.cell_type === "code" && cell.getIn(["metadata", "collapsed"]) === false;
|
|
}
|
|
}
|
|
|
|
return { outputs, hidden, expanded };
|
|
};
|
|
return mapStateToProps;
|
|
};
|
|
|
|
export const makeMapDispatchToProps = (
|
|
initialDispath: Dispatch,
|
|
ownProps: ComponentProps
|
|
): ((dispatch: Dispatch) => DispatchProps) => {
|
|
const { id, contentRef } = ownProps;
|
|
const mapDispatchToProps = (dispatch: Dispatch) => {
|
|
return {
|
|
onMetadataChange: (metadata: JSONObject, mediaType: string, index?: number) => {
|
|
dispatch(
|
|
actions.updateOutputMetadata({
|
|
id,
|
|
contentRef,
|
|
metadata,
|
|
index: index || 0,
|
|
mediaType,
|
|
})
|
|
);
|
|
},
|
|
};
|
|
};
|
|
return mapDispatchToProps;
|
|
};
|
|
|
|
export default connect<StateProps, DispatchProps, ComponentProps, AppState>(
|
|
makeMapStateToProps,
|
|
makeMapDispatchToProps
|
|
)(SandboxOutputs);
|