github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ui/src/components/downloadFile/downloadFile.tsx (about)

     1  // Copyright 2020 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  import React, { useRef, useEffect, forwardRef, useImperativeHandle } from "react";
    12  
    13  type FileTypes = "text/plain" | "application/json";
    14  
    15  export interface DownloadAsFileProps {
    16    fileName?: string;
    17    fileType?: FileTypes;
    18    content?: string;
    19  }
    20  
    21  export interface DownloadFileRef {
    22    download: (name: string, type: FileTypes, body: string) => void;
    23  }
    24  
    25  /*
    26  * DownloadFile can download file in two modes `default` and `imperative`.
    27  * `Default` mode - when DownloadFile wraps component which should trigger
    28  * downloading and can work only if content of file is already available.
    29  *
    30  * For example:
    31  * ```
    32  * <DownloadFile fileName="example.txt" fileType="text/plain" content="Some text">
    33  *   <button>Download</download>
    34  * </DownloadFile>
    35  * ```
    36  *
    37  * `Imperative` mode allows initiate file download in async way, and trigger
    38  * download manually.
    39  *
    40  * For example:
    41  * ```
    42  * downloadRef = React.createRef<DownloadFileRef>();
    43  *
    44  * fetchData = () => {
    45  *   Promise.resolve().then((someText) =>
    46  *     this.downloadRef.current.download("example.txt", "text/plain", someText))
    47  * }
    48  *
    49  * <DownloadFile ref={downloadRef} />
    50  * <button onClick={fetchData}>Download</button>
    51  * ```
    52  * */
    53  // tslint:disable-next-line:variable-name
    54  export const DownloadFile = forwardRef<DownloadFileRef, DownloadAsFileProps>((props, ref) => {
    55    const { children, fileName, fileType, content } = props;
    56    const anchorRef = useRef<HTMLAnchorElement>();
    57  
    58    const bootstrapFile = (name: string, type: FileTypes, body: string) => {
    59      const anchorElement = anchorRef.current;
    60      const file = new Blob([body], { type });
    61      anchorElement.href = URL.createObjectURL(file);
    62      anchorElement.download = name;
    63    };
    64  
    65    useEffect(
    66      () => {
    67        if (content === undefined) {
    68          return;
    69        }
    70        bootstrapFile(fileName, fileType, content);
    71      },
    72      [fileName, fileType, content],
    73    );
    74  
    75    useImperativeHandle(ref, () => ({
    76      download: (name: string, type: FileTypes, body: string) => {
    77        bootstrapFile(name, type, body);
    78        anchorRef.current.click();
    79      },
    80    }));
    81  
    82    return (
    83      <a ref={anchorRef}>
    84        {children}
    85      </a>
    86    );
    87  });