123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- import React from "react";
- import { toBlob, toPng } from "html-to-image";
- import { TwitterPicker } from "react-color";
- import { TwitterPickerStylesProps } from "react-color/lib/components/twitter/Twitter";
- import toast from "react-hot-toast";
- import { FiCopy, FiDownload } from "react-icons/fi";
- import { Button } from "src/components/Button";
- import { Input } from "src/components/Input";
- import { Modal, ModalProps } from "src/components/Modal";
- import styled from "styled-components";
- import useGraph from "src/store/useGraph";
- const ColorPickerStyles: Partial<TwitterPickerStylesProps> = {
- card: {
- background: "transparent",
- boxShadow: "none",
- },
- body: {
- padding: 0,
- },
- input: {
- background: "rgba(0, 0, 0, 0.2)",
- boxShadow: "none",
- textTransform: "none",
- whiteSpace: "nowrap",
- textOverflow: "ellipsis",
- },
- hash: {
- background: "rgba(180, 180, 180, 0.3)",
- },
- };
- const defaultColors = [
- "#B80000",
- "#DB3E00",
- "#FCCB00",
- "#008B02",
- "#006B76",
- "#1273DE",
- "#004DCF",
- "#5300EB",
- "#EB9694",
- "#FAD0C3",
- "#FEF3BD",
- "#C1E1C5",
- "#BEDADC",
- "#C4DEF6",
- "#BED3F3",
- "#D4C4FB",
- "transparent",
- ];
- function downloadURI(uri: string, name: string) {
- var link = document.createElement("a");
- link.download = name;
- link.href = uri;
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
- }
- const StyledContainer = styled.div`
- display: flex;
- flex-direction: column;
- gap: 16px;
- padding: 12px 0;
- border-top: 1px solid ${({ theme }) => theme.BACKGROUND_MODIFIER_ACCENT};
- font-size: 12px;
- line-height: 16px;
- font-weight: 600;
- text-transform: uppercase;
- color: ${({ theme }) => theme.INTERACTIVE_NORMAL};
- &:first-of-type {
- padding-top: 0;
- border: none;
- }
- `;
- const StyledColorWrapper = styled.div`
- display: flex;
- justify-content: space-between;
- `;
- const StyledColorIndicator = styled.div<{ color: string }>`
- flex: 1;
- width: 100%;
- height: auto;
- border-radius: 6px;
- background: ${({ color }) => color};
- border: 1px solid;
- border-color: rgba(0, 0, 0, 0.1);
- `;
- export const DownloadModal: React.FC<ModalProps> = ({ visible, setVisible }) => {
- const togglePerfMode = useGraph(state => state.togglePerfMode);
- const [fileDetails, setFileDetails] = React.useState({
- filename: "jsoncrack.com",
- backgroundColor: "transparent",
- quality: 1,
- });
- const clipboardImage = async () => {
- try {
- toast.loading("Copying to clipboard...", { id: "toastClipboard" });
- togglePerfMode(false);
- const imageElement = document.querySelector("svg[id*='ref']") as HTMLElement;
- const blob = await toBlob(imageElement, {
- quality: fileDetails.quality,
- backgroundColor: fileDetails.backgroundColor,
- });
- if (!blob) return;
- navigator.clipboard.write([
- new ClipboardItem({
- [blob.type]: blob,
- }),
- ]);
- toast.success("Copied to clipboard");
- } catch (error) {
- toast.error("Failed to copy to clipboard");
- } finally {
- toast.dismiss("toastClipboard");
- setVisible(false);
- togglePerfMode(true);
- }
- };
- const exportAsImage = async () => {
- try {
- toast.loading("Downloading...", { id: "toastDownload" });
- togglePerfMode(false);
- const imageElement = document.querySelector("svg[id*='ref']") as HTMLElement;
- const dataURI = await toPng(imageElement, {
- quality: fileDetails.quality,
- backgroundColor: fileDetails.backgroundColor,
- });
- downloadURI(dataURI, `${fileDetails.filename}.png`);
- } catch (error) {
- toast.error("Failed to download image!");
- } finally {
- toast.dismiss("toastDownload");
- setVisible(false);
- togglePerfMode(true);
- }
- };
- const updateDetails = (key: keyof typeof fileDetails, value: string | number) =>
- setFileDetails({ ...fileDetails, [key]: value });
- return (
- <Modal visible={visible} setVisible={setVisible}>
- <Modal.Header>Download Image</Modal.Header>
- <Modal.Content>
- <StyledContainer>
- File Name
- <StyledColorWrapper>
- <Input
- placeholder="File Name"
- value={fileDetails.filename}
- onChange={e => updateDetails("filename", e.target.value)}
- />
- </StyledColorWrapper>
- </StyledContainer>
- <StyledContainer>
- Background Color
- <StyledColorWrapper>
- <TwitterPicker
- triangle="hide"
- colors={defaultColors}
- color={fileDetails.backgroundColor}
- onChange={color => updateDetails("backgroundColor", color.hex)}
- styles={{
- default: ColorPickerStyles,
- }}
- />
- <StyledColorIndicator color={fileDetails.backgroundColor} />
- </StyledColorWrapper>
- </StyledContainer>
- </Modal.Content>
- <Modal.Controls setVisible={setVisible}>
- <Button status="SECONDARY" onClick={clipboardImage}>
- <FiCopy size={18} /> Clipboard
- </Button>
- <Button status="SUCCESS" onClick={exportAsImage}>
- <FiDownload size={18} />
- Download
- </Button>
- </Modal.Controls>
- </Modal>
- );
- };
|