UNPKG

@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
"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;