PasteChartDialog.tsx 3.0 KB

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