@thomasvu/react-jupyter-notebook
Version:
A simple React component that renders .ipynb files just like how they are rendered by Jupyter Lab
347 lines (303 loc) • 13.2 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/objectSpread2"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/slicedToArray"));
var _react = _interopRequireWildcard(require("react"));
var _ansiToReact = _interopRequireDefault(require("ansi-to-react"));
var _reactMarkdown = _interopRequireDefault(require("react-markdown"));
var _remarkGfm = _interopRequireDefault(require("remark-gfm"));
var _remarkMath = _interopRequireDefault(require("remark-math"));
var _rehypeKatex = _interopRequireDefault(require("rehype-katex"));
var _reactSyntaxHighlighter = _interopRequireDefault(require("react-syntax-highlighter"));
var _hljsStyles = _interopRequireDefault(require("./hljsStyles"));
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function BlockSource(props) {
var metadata = props.cell['metadata'];
var source = props.cell['source'];
var type = props.cell['cell_type'];
var _useState = (0, _react.useState)({
prevDisplay: 1,
display: 1,
contentHeight: 0
}),
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
state = _useState2[0],
setState = _useState2[1];
if (props.display !== state.prevDisplay) {
var newDisplay = props.display;
if (newDisplay === -1) {
if (metadata['jupyter'] !== undefined && metadata['jupyter']['source_hidden']) {
newDisplay = 0;
}
}
setState((0, _objectSpread2.default)((0, _objectSpread2.default)({}, state), {}, {
prevDisplay: props.display,
display: newDisplay
}));
}
var htmlContent;
var executionCount;
if (type === 'code') {
executionCount = props.cell['execution_count']; // SyntaxHighlighter originally doesn't separate the line numbers and the codes.
// The first SyntaxHighlighter is used to show line numbers only and the second is to show codes only.
var _ref = props.codeBlockStyles ? props.codeBlockStyles : {},
hljsStyle = _ref.hljsStyle,
lineNumberContainerStyle = _ref.lineNumberContainerStyle,
lineNumberStyle = _ref.lineNumberStyle,
codeContainerStyle = _ref.codeContainerStyle;
htmlContent = /*#__PURE__*/_react.default.createElement("div", {
className: "cell-content source-code"
}, !props.showLineNumbers ? null : /*#__PURE__*/_react.default.createElement(_reactSyntaxHighlighter.default, {
language: props.language,
style: hljsStyle ? _hljsStyles.default[hljsStyle] : _hljsStyles.default.vs,
codeTagProps: {
style: {
fontFamily: "Menlo, Consolas, 'DejaVu Sans Mono', monospace",
fontSize: "13px"
}
},
customStyle: hljsStyle ? lineNumberContainerStyle : {
width: "37px",
margin: "0 0 0 0",
padding: "5px 0 5px 0",
boxSizing: "border-box",
background: "#EEEEEE",
border: "solid 1px #E0E0E0",
overflow: "hidden"
},
showLineNumbers: true,
lineNumberStyle: hljsStyle ? lineNumberStyle : {
width: "37px",
padding: "0 8px 0 8px",
boxSizing: "border-box",
color: "#999999"
}
}, source.map(function (item, index) {
return index === 0 ? ' ' : '\n';
}).join('')), /*#__PURE__*/_react.default.createElement("div", {
className: "source-code-main"
}, /*#__PURE__*/_react.default.createElement(_reactSyntaxHighlighter.default, {
language: props.language,
style: hljsStyle ? _hljsStyles.default[hljsStyle] : _hljsStyles.default.vs,
codeTagProps: {
style: {
fontFamily: "Menlo, Consolas, 'DejaVu Sans Mono', monospace",
fontSize: "13px"
}
},
customStyle: hljsStyle ? codeContainerStyle : {
margin: "0 0 0 0",
padding: "5px 4px 5px 4px",
boxSizing: "border-box",
background: "#F5F5F5",
border: "solid 1px #E0E0E0",
flex: 1
}
}, source.join(''))));
} else if (type === 'markdown') {
// '$$' has to be in a separate new line to be rendered as a block math equation.
var re = /\n?\s*\$\$\s*\n?/g;
var newSource = source.join('').replaceAll(re, "\n$$$\n");
htmlContent = /*#__PURE__*/_react.default.createElement("div", {
className: "cell-content source-markdown"
}, /*#__PURE__*/_react.default.createElement(_reactMarkdown.default, {
remarkPlugins: [_remarkGfm.default, _remarkMath.default],
rehypePlugins: [_rehypeKatex.default]
}, newSource));
} else {
htmlContent = /*#__PURE__*/_react.default.createElement("div", null, "Cell Type ".concat(type, " not supported..."));
}
return /*#__PURE__*/_react.default.createElement("div", {
className: "block-source"
}, /*#__PURE__*/_react.default.createElement("div", {
className: props.highlighted ? "block-light-selected" : "block-light",
onClick: function onClick() {
setState((0, _objectSpread2.default)((0, _objectSpread2.default)({}, state), {}, {
display: (state.display + 1) % 2
}));
}
}), state.display === 0 ? /*#__PURE__*/_react.default.createElement("div", {
className: "block-hidden"
}) : /*#__PURE__*/_react.default.createElement("div", {
className: "cell-row"
}, /*#__PURE__*/_react.default.createElement("pre", {
className: "cell-header source"
}, executionCount ? "[".concat(executionCount, "]: ") : null), htmlContent));
}
function BlockOutput(props) {
var metadata = props.cell['metadata'];
var outputs = props.cell['outputs'];
var _useState3 = (0, _react.useState)({
highlighted: false,
prevDisplay: 1,
display: 1,
contentHeight: 0
}),
_useState4 = (0, _slicedToArray2.default)(_useState3, 2),
state = _useState4[0],
setState = _useState4[1];
var contentRef = (0, _react.useCallback)(function (node) {
if (node) {
setState(function (state) {
return (0, _objectSpread2.default)((0, _objectSpread2.default)({}, state), {}, {
contentHeight: node.offsetHeight
});
});
}
}, []);
if (props.display !== state.prevDisplay) {
var newDisplay = props.display;
if (newDisplay === -1) {
if (metadata['collapsed'] || metadata['jupyter'] !== undefined && metadata['jupyter']['outputs_hidden']) {
newDisplay = 0;
} else if (metadata['scrolled']) {
newDisplay = 2;
}
}
setState((0, _objectSpread2.default)((0, _objectSpread2.default)({}, state), {}, {
prevDisplay: props.display,
display: newDisplay
}));
}
return /*#__PURE__*/_react.default.createElement("div", {
className: "block-output"
}, /*#__PURE__*/_react.default.createElement("div", {
className: props.highlighted ? "block-light-selected" : "block-light",
onClick: function onClick() {
setState((0, _objectSpread2.default)((0, _objectSpread2.default)({}, state), {}, {
display: (state.display + 1) % 3
}));
}
}), state.display === 0 ? /*#__PURE__*/_react.default.createElement("div", {
className: "block-hidden"
}) : /*#__PURE__*/_react.default.createElement("div", {
className: "block-output-content",
style: state.display === 2 ? {
maxHeight: state.contentHeight,
height: 200,
boxShadow: "inset 0 0 6px 2px rgb(0 0 0 / 30%)",
resize: "vertical"
} : null
}, /*#__PURE__*/_react.default.createElement("div", {
ref: contentRef
}, outputs.map(function (output, index) {
var executionCount;
var htmlContent;
if ('output_type' in output) {
var output_type = output['output_type'];
switch (output_type) {
// Stdout and stderr
case 'stream':
htmlContent = /*#__PURE__*/_react.default.createElement("pre", {
className: "cell-content ".concat(output['name'] === 'stdout' ? 'output-std' : 'output-err')
}, output['text'].join(''));
break;
// Output with execution_count
case 'execute_result':
executionCount = output['execution_count'];
// Output without execution_count
case 'display_data':
var output_data = output['data'];
if ('image/png' in output_data) {
var output_metadata = output['metadata'];
var size = output_metadata && output_metadata['image/png'];
htmlContent = /*#__PURE__*/_react.default.createElement("div", {
className: "cell-content output-display",
style: {
justifyContent: props.mediaAlign
}
}, /*#__PURE__*/_react.default.createElement("img", {
src: "data:image/png;base64,".concat(output_data['image/png']),
width: size ? size['width'] : 'auto',
height: size ? size['height'] : 'auto',
alt: ""
}));
} else if ('text/html' in output_data) {
htmlContent = /*#__PURE__*/_react.default.createElement("div", {
className: "cell-content output-display",
style: {
justifyContent: props.mediaAlign
},
dangerouslySetInnerHTML: {
__html: output_data['text/html'].join('')
}
});
} else if ('text/plain' in output_data) {
htmlContent = /*#__PURE__*/_react.default.createElement("pre", {
className: "cell-content output-std"
}, output_data['text/plain'].join(''));
}
break;
// Exceptions
case 'error':
var output_traceback = output['traceback'].join('\n');
htmlContent = /*#__PURE__*/_react.default.createElement("pre", {
className: "cell-content output-err"
}, /*#__PURE__*/_react.default.createElement(_ansiToReact.default, null, output_traceback));
break;
default:
console.log('Unexpected output_type: ', output_type);
}
}
return /*#__PURE__*/_react.default.createElement("div", {
key: index,
className: "cell-row"
}, /*#__PURE__*/_react.default.createElement("pre", {
className: "cell-header output"
}, executionCount ? "[".concat(executionCount, "]: ") : null), htmlContent);
}))));
}
function JupyterViewer(props) {
// -1: auto, 0: hide, 1: show, 2: scroll
var DISPLAYS = ['hide', 'show', 'scroll'];
var _useState5 = (0, _react.useState)({
clickCellIndex: -1
}),
_useState6 = (0, _slicedToArray2.default)(_useState5, 2),
state = _useState6[0],
setState = _useState6[1];
return /*#__PURE__*/_react.default.createElement("div", {
className: "jupyter-viewer"
}, props.rawIpynb['cells'].map(function (cell, index) {
return /*#__PURE__*/_react.default.createElement("div", {
key: index,
className: "block",
onMouseDown: function onMouseDown() {
setState((0, _objectSpread2.default)((0, _objectSpread2.default)({}, state), {}, {
clickCellIndex: index
}));
}
}, !('cell_type' in cell) ? null : /*#__PURE__*/_react.default.createElement(BlockSource, {
cell: cell,
language: props.language,
highlighted: state.clickCellIndex === index,
display: DISPLAYS.indexOf(props.displaySource),
showLineNumbers: props.showLineNumbers,
codeBlockStyles: props.codeBlockStyles
}), !('outputs' in cell) ? null : /*#__PURE__*/_react.default.createElement(BlockOutput, {
cell: cell,
highlighted: state.clickCellIndex === index,
display: DISPLAYS.indexOf(props.displayOutput),
mediaAlign: {
left: 'flex-start',
center: 'center',
right: 'flex-end'
}[props.mediaAlign]
}));
}));
}
JupyterViewer.defaultProps = {
language: 'python',
showLineNumbers: true,
mediaAlign: 'center',
displaySource: 'auto',
displayOutput: 'auto',
codeBlockStyles: undefined
};
var _default = JupyterViewer;
exports.default = _default;