import { ImmutableNotebook } from "@nteract/commutable"; import Html2Canvas from "html2canvas"; import { Dropdown, IDropdownProps, ITextFieldProps, Stack, Text, TextField } from "office-ui-fabric-react"; import React, { FunctionComponent, useState } from "react"; import { GalleryCardComponent } from "../../Controls/NotebookGallery/Cards/GalleryCardComponent"; import * as FileSystemUtil from "../../Notebook/FileSystemUtil"; import { NotebookUtil } from "../../Notebook/NotebookUtil"; import "./styled.less"; export interface PublishNotebookPaneProps { notebookName: string; notebookAuthor: string; notebookTags: string; imageSrc: string; notebookDescription: string; notebookCreatedDate: string; notebookObject: ImmutableNotebook; notebookParentDomElement?: HTMLElement; onError: (formError: string, formErrorDetail: string, area: string) => void; clearFormError: () => void; setNotebookName: (newValue: string) => void; setNotebookDescription: (newValue: string) => void; setNotebookTags: (newValue: string) => void; setImageSrc: (newValue: string) => void; } enum ImageTypes { Url = "URL", CustomImage = "Custom Image", TakeScreenshot = "Take Screenshot", UseFirstDisplayOutput = "Use First Display Output", } export const PublishNotebookPaneComponent: FunctionComponent = ({ notebookName, notebookTags, imageSrc, notebookDescription, notebookAuthor, notebookCreatedDate, notebookObject, notebookParentDomElement, onError, clearFormError, setNotebookName, setNotebookDescription, setNotebookTags, setImageSrc, }: PublishNotebookPaneProps) => { const [type, setType] = useState(ImageTypes.CustomImage); const CARD_WIDTH = 256; const cardImageHeight = 144; const cardHeightToWidthRatio = cardImageHeight / CARD_WIDTH; const maxImageSizeInMib = 1.5; const descriptionPara1 = "When published, this notebook will appear in the Azure Cosmos DB notebooks public gallery. Make sure you have removed any sensitive data or output before publishing."; const descriptionPara2 = `Would you like to publish and share "${FileSystemUtil.stripExtension( notebookName, "ipynb" )}" to the gallery?`; const options: ImageTypes[] = [ImageTypes.CustomImage, ImageTypes.Url]; const thumbnailSelectorProps: IDropdownProps = { label: "Cover image", defaultSelectedKey: ImageTypes.CustomImage, ariaLabel: "Cover image", options: options.map((value: string) => ({ text: value, key: value })), onChange: async (event, options) => { setImageSrc(""); clearFormError(); if (options.text === ImageTypes.TakeScreenshot) { try { await takeScreenshot(notebookParentDomElement, screenshotErrorHandler); } catch (error) { screenshotErrorHandler(error); } } else if (options.text === ImageTypes.UseFirstDisplayOutput) { try { await takeScreenshot(findFirstOutput(), firstOutputErrorHandler); } catch (error) { firstOutputErrorHandler(error); } } setType(options.text); }, }; const thumbnailUrlProps: ITextFieldProps = { label: "Cover image url", ariaLabel: "Cover image url", required: true, onChange: (event, newValue) => { setImageSrc(newValue); }, }; const screenshotErrorHandler = (error: Error) => { const formError = "Failed to take screen shot"; const formErrorDetail = `${error}`; const area = "PublishNotebookPaneComponent/takeScreenshot"; onError(formError, formErrorDetail, area); }; const firstOutputErrorHandler = (error: Error) => { const formError = "Failed to capture first output"; const formErrorDetail = `${error}`; const area = "PublishNotebookPaneComponent/UseFirstOutput"; onError(formError, formErrorDetail, area); }; if (notebookParentDomElement) { options.push(ImageTypes.TakeScreenshot); if (notebookObject) { options.push(ImageTypes.UseFirstDisplayOutput); } } const imageToBase64 = (file: File, updateImageSrc: (result: string) => void) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => { updateImageSrc(reader.result.toString()); }; reader.onerror = (error) => { const formError = `Failed to convert ${file.name} to base64 format`; const formErrorDetail = `${error}`; const area = "PublishNotebookPaneComponent/selectImageFile"; onError(formError, formErrorDetail, area); }; }; const takeScreenshot = (target: HTMLElement, onError: (error: Error) => void): void => { const updateImageSrcWithScreenshot = (canvasUrl: string): void => { setImageSrc(canvasUrl); }; target.scrollIntoView(); Html2Canvas(target, { useCORS: true, allowTaint: true, scale: 1, logging: true, }) .then((canvas) => { //redraw canvas to fit Card Cover Image dimensions const originalImageData = canvas.toDataURL(); const requiredHeight = parseInt(canvas.style.width.split("px")[0]) * cardHeightToWidthRatio; canvas.height = requiredHeight; const context = canvas.getContext("2d"); const image = new Image(); image.src = originalImageData; image.onload = () => { context.drawImage(image, 0, 0); updateImageSrcWithScreenshot(canvas.toDataURL()); }; }) .catch((error) => { onError(error); }); }; const renderThumbnailSelectors = (type: string) => { switch (type) { case ImageTypes.Url: return ; case ImageTypes.CustomImage: return ( { const file = event.target.files[0]; if (file.size / 1024 ** 2 > maxImageSizeInMib) { event.target.value = ""; const formError = `Failed to upload ${file.name}`; const formErrorDetail = `Image is larger than ${maxImageSizeInMib} MiB. Please Choose a different image.`; const area = "PublishNotebookPaneComponent/selectImageFile"; onError(formError, formErrorDetail, area); setImageSrc(""); return; } else { clearFormError(); } imageToBase64(file, (result: string) => { setImageSrc(result); }); }} /> ); default: return <>; } }; const findFirstOutput = (): HTMLElement => { const indexOfFirstCodeCellWithDisplay = NotebookUtil.findFirstCodeCellWithDisplay(notebookObject); const cellOutputDomElements = notebookParentDomElement.querySelectorAll(".nteract-cell-outputs"); return cellOutputDomElements[indexOfFirstCodeCellWithDisplay]; }; return (
{descriptionPara1} {descriptionPara2} { const notebookName = newValue + ".ipynb"; setNotebookName(notebookName); }} /> { setNotebookDescription(newValue); }} /> { setNotebookTags(newValue); }} /> {renderThumbnailSelectors(type)} Preview undefined} onTagClick={undefined} onFavoriteClick={undefined} onUnfavoriteClick={undefined} onDownloadClick={undefined} onDeleteClick={undefined} />
); };