JSONExportDialog.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import React, { useState } from "react";
  2. import { NonDeletedExcalidrawElement } from "../element/types";
  3. import { t } from "../i18n";
  4. import { useDeviceType } from "./App";
  5. import { AppState, ExportOpts, BinaryFiles } from "../types";
  6. import { Dialog } from "./Dialog";
  7. import { exportFile, exportToFileIcon, link } from "./icons";
  8. import { ToolButton } from "./ToolButton";
  9. import { actionSaveFileToDisk } from "../actions/actionExport";
  10. import { Card } from "./Card";
  11. import "./ExportDialog.scss";
  12. import { nativeFileSystemSupported } from "../data/filesystem";
  13. import { trackEvent } from "../analytics";
  14. import { ActionManager } from "../actions/manager";
  15. import { getFrame } from "../utils";
  16. export type ExportCB = (
  17. elements: readonly NonDeletedExcalidrawElement[],
  18. scale?: number,
  19. ) => void;
  20. const JSONExportModal = ({
  21. elements,
  22. appState,
  23. files,
  24. actionManager,
  25. exportOpts,
  26. canvas,
  27. }: {
  28. appState: AppState;
  29. files: BinaryFiles;
  30. elements: readonly NonDeletedExcalidrawElement[];
  31. actionManager: ActionManager;
  32. onCloseRequest: () => void;
  33. exportOpts: ExportOpts;
  34. canvas: HTMLCanvasElement | null;
  35. }) => {
  36. const { onExportToBackend } = exportOpts;
  37. return (
  38. <div className="ExportDialog ExportDialog--json">
  39. <div className="ExportDialog-cards">
  40. {exportOpts.saveFileToDisk && (
  41. <Card color="lime">
  42. <div className="Card-icon">{exportToFileIcon}</div>
  43. <h2>{t("exportDialog.disk_title")}</h2>
  44. <div className="Card-details">
  45. {t("exportDialog.disk_details")}
  46. {!nativeFileSystemSupported &&
  47. actionManager.renderAction("changeProjectName")}
  48. </div>
  49. <ToolButton
  50. className="Card-button"
  51. type="button"
  52. title={t("exportDialog.disk_button")}
  53. aria-label={t("exportDialog.disk_button")}
  54. showAriaLabel={true}
  55. onClick={() => {
  56. actionManager.executeAction(actionSaveFileToDisk, "ui");
  57. }}
  58. />
  59. </Card>
  60. )}
  61. {onExportToBackend && (
  62. <Card color="pink">
  63. <div className="Card-icon">{link}</div>
  64. <h2>{t("exportDialog.link_title")}</h2>
  65. <div className="Card-details">{t("exportDialog.link_details")}</div>
  66. <ToolButton
  67. className="Card-button"
  68. type="button"
  69. title={t("exportDialog.link_button")}
  70. aria-label={t("exportDialog.link_button")}
  71. showAriaLabel={true}
  72. onClick={() => {
  73. onExportToBackend(elements, appState, files, canvas);
  74. trackEvent("export", "link", `ui (${getFrame()})`);
  75. }}
  76. />
  77. </Card>
  78. )}
  79. {exportOpts.renderCustomUI &&
  80. exportOpts.renderCustomUI(elements, appState, files, canvas)}
  81. </div>
  82. </div>
  83. );
  84. };
  85. export const JSONExportDialog = ({
  86. elements,
  87. appState,
  88. files,
  89. actionManager,
  90. exportOpts,
  91. canvas,
  92. }: {
  93. elements: readonly NonDeletedExcalidrawElement[];
  94. appState: AppState;
  95. files: BinaryFiles;
  96. actionManager: ActionManager;
  97. exportOpts: ExportOpts;
  98. canvas: HTMLCanvasElement | null;
  99. }) => {
  100. const [modalIsShown, setModalIsShown] = useState(false);
  101. const handleClose = React.useCallback(() => {
  102. setModalIsShown(false);
  103. }, []);
  104. return (
  105. <>
  106. <ToolButton
  107. onClick={() => {
  108. setModalIsShown(true);
  109. }}
  110. data-testid="json-export-button"
  111. icon={exportFile}
  112. type="button"
  113. aria-label={t("buttons.export")}
  114. showAriaLabel={useDeviceType().isMobile}
  115. title={t("buttons.export")}
  116. />
  117. {modalIsShown && (
  118. <Dialog onCloseRequest={handleClose} title={t("buttons.export")}>
  119. <JSONExportModal
  120. elements={elements}
  121. appState={appState}
  122. files={files}
  123. actionManager={actionManager}
  124. onCloseRequest={handleClose}
  125. exportOpts={exportOpts}
  126. canvas={canvas}
  127. />
  128. </Dialog>
  129. )}
  130. </>
  131. );
  132. };