mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-26 12:21:23 +00:00
* Added support for taking screenshot - Screenshot is taken using html2canvas package - Converted to base 64 and uploaded to metadata - For Using first display output - Notebok object is passed instead of string, to publish pane - The first cell with output present is parsed out - The dom is also parsed to get corresponding div element to take screenshot of the first output * fixed bug * Addressed PR comments - FIxed bug that didn't capture screenshot when mutiple notebook tabs are opened * removed unnecessary dependencies * fixed compile issues * more edits
199 lines
6.5 KiB
TypeScript
199 lines
6.5 KiB
TypeScript
import { Card } from "@uifabric/react-cards";
|
|
import {
|
|
FontWeights,
|
|
Icon,
|
|
IconButton,
|
|
Image,
|
|
ImageFit,
|
|
Persona,
|
|
Text,
|
|
Link,
|
|
BaseButton,
|
|
Button,
|
|
LinkBase,
|
|
Separator,
|
|
TooltipHost
|
|
} from "office-ui-fabric-react";
|
|
import * as React from "react";
|
|
import { IGalleryItem } from "../../../../Juno/JunoClient";
|
|
import { FileSystemUtil } from "../../../Notebook/FileSystemUtil";
|
|
import CosmosDBLogo from "../../../../../images/CosmosDB-logo.svg";
|
|
import { StyleConstants } from "../../../../Common/Constants";
|
|
|
|
export interface GalleryCardComponentProps {
|
|
data: IGalleryItem;
|
|
isFavorite: boolean;
|
|
showDownload: boolean;
|
|
showDelete: boolean;
|
|
onClick: () => void;
|
|
onTagClick: (tag: string) => void;
|
|
onFavoriteClick: () => void;
|
|
onUnfavoriteClick: () => void;
|
|
onDownloadClick: () => void;
|
|
onDeleteClick: () => void;
|
|
}
|
|
|
|
export class GalleryCardComponent extends React.Component<GalleryCardComponentProps> {
|
|
public static readonly CARD_WIDTH = 256;
|
|
private static readonly cardImageHeight = 144;
|
|
public static readonly cardHeightToWidthRatio =
|
|
GalleryCardComponent.cardImageHeight / GalleryCardComponent.CARD_WIDTH;
|
|
private static readonly cardDescriptionMaxChars = 88;
|
|
private static readonly cardItemGapBig = 10;
|
|
private static readonly cardItemGapSmall = 8;
|
|
|
|
public render(): JSX.Element {
|
|
const cardButtonsVisible = this.props.isFavorite !== undefined || this.props.showDownload || this.props.showDelete;
|
|
const options: Intl.DateTimeFormatOptions = {
|
|
year: "numeric",
|
|
month: "short",
|
|
day: "numeric"
|
|
};
|
|
const dateString = new Date(this.props.data.created).toLocaleString("default", options);
|
|
const cardTitle = FileSystemUtil.stripExtension(this.props.data.name, "ipynb");
|
|
|
|
return (
|
|
<Card
|
|
aria-label={cardTitle}
|
|
data-is-focusable="true"
|
|
tokens={{ width: GalleryCardComponent.CARD_WIDTH, childrenGap: 0 }}
|
|
onClick={event => this.onClick(event, this.props.onClick)}
|
|
>
|
|
<Card.Item tokens={{ padding: GalleryCardComponent.cardItemGapBig }}>
|
|
<Persona
|
|
imageUrl={this.props.data.isSample && CosmosDBLogo}
|
|
text={this.props.data.author}
|
|
secondaryText={dateString}
|
|
/>
|
|
</Card.Item>
|
|
|
|
<Card.Item>
|
|
<Image
|
|
src={this.props.data.thumbnailUrl}
|
|
width={GalleryCardComponent.CARD_WIDTH}
|
|
height={GalleryCardComponent.cardImageHeight}
|
|
imageFit={ImageFit.cover}
|
|
alt={`${cardTitle} cover image`}
|
|
/>
|
|
</Card.Item>
|
|
|
|
<Card.Section styles={{ root: { padding: GalleryCardComponent.cardItemGapBig } }}>
|
|
<Text variant="small" nowrap>
|
|
{this.props.data.tags?.map((tag, index, array) => (
|
|
<span key={tag}>
|
|
<Link onClick={event => this.onClick(event, () => this.props.onTagClick(tag))}>{tag}</Link>
|
|
{index === array.length - 1 ? <></> : ", "}
|
|
</span>
|
|
))}
|
|
</Text>
|
|
|
|
<Text
|
|
styles={{
|
|
root: {
|
|
fontWeight: FontWeights.semibold,
|
|
paddingTop: GalleryCardComponent.cardItemGapSmall,
|
|
paddingBottom: GalleryCardComponent.cardItemGapSmall
|
|
}
|
|
}}
|
|
nowrap
|
|
>
|
|
{cardTitle}
|
|
</Text>
|
|
|
|
<Text variant="small" styles={{ root: { height: 36 } }}>
|
|
{this.props.data.description.substr(0, GalleryCardComponent.cardDescriptionMaxChars)}
|
|
</Text>
|
|
|
|
<span>
|
|
{this.generateIconText("RedEye", this.props.data.views.toString())}
|
|
{this.generateIconText("Download", this.props.data.downloads.toString())}
|
|
{this.props.isFavorite !== undefined &&
|
|
this.generateIconText("Heart", this.props.data.favorites.toString())}
|
|
</span>
|
|
</Card.Section>
|
|
|
|
{cardButtonsVisible && (
|
|
<Card.Section
|
|
styles={{
|
|
root: {
|
|
marginLeft: GalleryCardComponent.cardItemGapBig,
|
|
marginRight: GalleryCardComponent.cardItemGapBig
|
|
}
|
|
}}
|
|
>
|
|
<Separator styles={{ root: { padding: 0, height: 1 } }} />
|
|
|
|
<span>
|
|
{this.props.isFavorite !== undefined &&
|
|
this.generateIconButtonWithTooltip(
|
|
this.props.isFavorite ? "HeartFill" : "Heart",
|
|
this.props.isFavorite ? "Unlike" : "Like",
|
|
"left",
|
|
this.props.isFavorite ? this.props.onUnfavoriteClick : this.props.onFavoriteClick
|
|
)}
|
|
|
|
{this.props.showDownload &&
|
|
this.generateIconButtonWithTooltip("Download", "Download", "left", this.props.onDownloadClick)}
|
|
|
|
{this.props.showDelete &&
|
|
this.generateIconButtonWithTooltip("Delete", "Remove", "right", this.props.onDeleteClick)}
|
|
</span>
|
|
</Card.Section>
|
|
)}
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
private generateIconText = (iconName: string, text: string): JSX.Element => {
|
|
return (
|
|
<Text
|
|
variant="tiny"
|
|
styles={{ root: { color: StyleConstants.BaseMedium, paddingRight: GalleryCardComponent.cardItemGapSmall } }}
|
|
>
|
|
<Icon iconName={iconName} styles={{ root: { verticalAlign: "middle" } }} /> {text}
|
|
</Text>
|
|
);
|
|
};
|
|
|
|
/*
|
|
* Fluent UI doesn't support tooltips on IconButtons out of the box. In the meantime the recommendation is
|
|
* to do the following (from https://developer.microsoft.com/en-us/fluentui#/controls/web/button)
|
|
*/
|
|
private generateIconButtonWithTooltip = (
|
|
iconName: string,
|
|
title: string,
|
|
horizontalAlign: "right" | "left",
|
|
activate: () => void
|
|
): JSX.Element => {
|
|
return (
|
|
<TooltipHost
|
|
content={title}
|
|
id={`TooltipHost-IconButton-${iconName}`}
|
|
calloutProps={{ gapSpace: 0 }}
|
|
styles={{ root: { display: "inline-block", float: horizontalAlign } }}
|
|
>
|
|
<IconButton
|
|
iconProps={{ iconName }}
|
|
title={title}
|
|
ariaLabel={title}
|
|
onClick={event => this.onClick(event, activate)}
|
|
/>
|
|
</TooltipHost>
|
|
);
|
|
};
|
|
|
|
private onClick = (
|
|
event:
|
|
| React.MouseEvent<HTMLElement | HTMLAnchorElement | HTMLButtonElement | LinkBase, MouseEvent>
|
|
| React.MouseEvent<
|
|
HTMLAnchorElement | HTMLButtonElement | HTMLDivElement | BaseButton | Button | HTMLSpanElement,
|
|
MouseEvent
|
|
>,
|
|
activate: () => void
|
|
): void => {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
activate();
|
|
};
|
|
}
|