PasteChartDialog.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import oc from "open-color";
  2. import React, { useLayoutEffect, useRef, useState } from "react";
  3. import { trackEvent } from "../analytics";
  4. import { ChartElements, renderSpreadsheet, Spreadsheet } from "../charts";
  5. import { ChartType } from "../element/types";
  6. import { t } from "../i18n";
  7. import { exportToSvg } from "../scene/export";
  8. import { AppState, LibraryItem } from "../types";
  9. import { Dialog } from "./Dialog";
  10. import "./PasteChartDialog.scss";
  11. type OnInsertChart = (chartType: ChartType, elements: ChartElements) => void;
  12. const ChartPreviewBtn = (props: {
  13. spreadsheet: Spreadsheet | null;
  14. chartType: ChartType;
  15. selected: boolean;
  16. onClick: OnInsertChart;
  17. }) => {
  18. const previewRef = useRef<HTMLDivElement | null>(null);
  19. const [chartElements, setChartElements] = useState<ChartElements | null>(
  20. null,
  21. );
  22. useLayoutEffect(() => {
  23. if (!props.spreadsheet) {
  24. return;
  25. }
  26. const elements = renderSpreadsheet(
  27. props.chartType,
  28. props.spreadsheet,
  29. 0,
  30. 0,
  31. );
  32. setChartElements(elements);
  33. let svg: SVGSVGElement;
  34. const previewNode = previewRef.current!;
  35. (async () => {
  36. svg = await exportToSvg(
  37. elements,
  38. {
  39. exportBackground: false,
  40. viewBackgroundColor: oc.white,
  41. },
  42. null, // files
  43. );
  44. previewNode.appendChild(svg);
  45. if (props.selected) {
  46. (previewNode.parentNode as HTMLDivElement).focus();
  47. }
  48. })();
  49. return () => {
  50. previewNode.removeChild(svg);
  51. };
  52. }, [props.spreadsheet, props.chartType, props.selected]);
  53. return (
  54. <button
  55. className="ChartPreview"
  56. onClick={() => {
  57. if (chartElements) {
  58. props.onClick(props.chartType, chartElements);
  59. }
  60. }}
  61. >
  62. <div ref={previewRef} />
  63. </button>
  64. );
  65. };
  66. export const PasteChartDialog = ({
  67. setAppState,
  68. appState,
  69. onClose,
  70. onInsertChart,
  71. }: {
  72. appState: AppState;
  73. onClose: () => void;
  74. setAppState: React.Component<any, AppState>["setState"];
  75. onInsertChart: (elements: LibraryItem["elements"]) => void;
  76. }) => {
  77. const handleClose = React.useCallback(() => {
  78. if (onClose) {
  79. onClose();
  80. }
  81. }, [onClose]);
  82. const handleChartClick = (chartType: ChartType, elements: ChartElements) => {
  83. onInsertChart(elements);
  84. trackEvent("magic", "chart", chartType);
  85. setAppState({
  86. currentChartType: chartType,
  87. pasteDialog: {
  88. shown: false,
  89. data: null,
  90. },
  91. });
  92. };
  93. return (
  94. <Dialog
  95. small
  96. onCloseRequest={handleClose}
  97. title={t("labels.pasteCharts")}
  98. className={"PasteChartDialog"}
  99. autofocus={false}
  100. >
  101. <div className={"container"}>
  102. <ChartPreviewBtn
  103. chartType="bar"
  104. spreadsheet={appState.pasteDialog.data}
  105. selected={appState.currentChartType === "bar"}
  106. onClick={handleChartClick}
  107. />
  108. <ChartPreviewBtn
  109. chartType="line"
  110. spreadsheet={appState.pasteDialog.data}
  111. selected={appState.currentChartType === "line"}
  112. onClick={handleChartClick}
  113. />
  114. </div>
  115. </Dialog>
  116. );
  117. };