PasteChartDialog.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  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. svg.querySelector(".style-fonts")?.remove();
  45. previewNode.replaceChildren();
  46. previewNode.appendChild(svg);
  47. if (props.selected) {
  48. (previewNode.parentNode as HTMLDivElement).focus();
  49. }
  50. })();
  51. return () => {
  52. previewNode.replaceChildren();
  53. };
  54. }, [props.spreadsheet, props.chartType, props.selected]);
  55. return (
  56. <button
  57. className="ChartPreview"
  58. onClick={() => {
  59. if (chartElements) {
  60. props.onClick(props.chartType, chartElements);
  61. }
  62. }}
  63. >
  64. <div ref={previewRef} />
  65. </button>
  66. );
  67. };
  68. export const PasteChartDialog = ({
  69. setAppState,
  70. appState,
  71. onClose,
  72. onInsertChart,
  73. }: {
  74. appState: AppState;
  75. onClose: () => void;
  76. setAppState: React.Component<any, AppState>["setState"];
  77. onInsertChart: (elements: LibraryItem["elements"]) => void;
  78. }) => {
  79. const handleClose = React.useCallback(() => {
  80. if (onClose) {
  81. onClose();
  82. }
  83. }, [onClose]);
  84. const handleChartClick = (chartType: ChartType, elements: ChartElements) => {
  85. onInsertChart(elements);
  86. trackEvent("magic", "chart", chartType);
  87. setAppState({
  88. currentChartType: chartType,
  89. pasteDialog: {
  90. shown: false,
  91. data: null,
  92. },
  93. });
  94. };
  95. return (
  96. <Dialog
  97. small
  98. onCloseRequest={handleClose}
  99. title={t("labels.pasteCharts")}
  100. className={"PasteChartDialog"}
  101. autofocus={false}
  102. >
  103. <div className={"container"}>
  104. <ChartPreviewBtn
  105. chartType="bar"
  106. spreadsheet={appState.pasteDialog.data}
  107. selected={appState.currentChartType === "bar"}
  108. onClick={handleChartClick}
  109. />
  110. <ChartPreviewBtn
  111. chartType="line"
  112. spreadsheet={appState.pasteDialog.data}
  113. selected={appState.currentChartType === "line"}
  114. onClick={handleChartClick}
  115. />
  116. </div>
  117. </Dialog>
  118. );
  119. };