actionExport.tsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import React from "react";
  2. import { trackEvent } from "../analytics";
  3. import { load, questionCircle, save, saveAs } from "../components/icons";
  4. import { ProjectName } from "../components/ProjectName";
  5. import { ToolButton } from "../components/ToolButton";
  6. import "../components/ToolIcon.scss";
  7. import { Tooltip } from "../components/Tooltip";
  8. import { loadFromJSON, saveAsJSON } from "../data";
  9. import { t } from "../i18n";
  10. import useIsMobile from "../is-mobile";
  11. import { KEYS } from "../keys";
  12. import { muteFSAbortError } from "../utils";
  13. import { register } from "./register";
  14. export const actionChangeProjectName = register({
  15. name: "changeProjectName",
  16. perform: (_elements, appState, value) => {
  17. trackEvent("change", "title");
  18. return { appState: { ...appState, name: value }, commitToHistory: false };
  19. },
  20. PanelComponent: ({ appState, updateData }) => (
  21. <ProjectName
  22. label={t("labels.fileTitle")}
  23. value={appState.name || "Unnamed"}
  24. onChange={(name: string) => updateData(name)}
  25. />
  26. ),
  27. });
  28. export const actionChangeExportBackground = register({
  29. name: "changeExportBackground",
  30. perform: (_elements, appState, value) => {
  31. return {
  32. appState: { ...appState, exportBackground: value },
  33. commitToHistory: false,
  34. };
  35. },
  36. PanelComponent: ({ appState, updateData }) => (
  37. <label>
  38. <input
  39. type="checkbox"
  40. checked={appState.exportBackground}
  41. onChange={(event) => updateData(event.target.checked)}
  42. />{" "}
  43. {t("labels.withBackground")}
  44. </label>
  45. ),
  46. });
  47. export const actionChangeExportEmbedScene = register({
  48. name: "changeExportEmbedScene",
  49. perform: (_elements, appState, value) => {
  50. return {
  51. appState: { ...appState, exportEmbedScene: value },
  52. commitToHistory: false,
  53. };
  54. },
  55. PanelComponent: ({ appState, updateData }) => (
  56. <label style={{ display: "flex" }}>
  57. <input
  58. type="checkbox"
  59. checked={appState.exportEmbedScene}
  60. onChange={(event) => updateData(event.target.checked)}
  61. />{" "}
  62. {t("labels.exportEmbedScene")}
  63. <Tooltip
  64. label={t("labels.exportEmbedScene_details")}
  65. position="above"
  66. long={true}
  67. >
  68. <div className="TooltipIcon">{questionCircle}</div>
  69. </Tooltip>
  70. </label>
  71. ),
  72. });
  73. export const actionChangeShouldAddWatermark = register({
  74. name: "changeShouldAddWatermark",
  75. perform: (_elements, appState, value) => {
  76. return {
  77. appState: { ...appState, shouldAddWatermark: value },
  78. commitToHistory: false,
  79. };
  80. },
  81. PanelComponent: ({ appState, updateData }) => (
  82. <label>
  83. <input
  84. type="checkbox"
  85. checked={appState.shouldAddWatermark}
  86. onChange={(event) => updateData(event.target.checked)}
  87. />{" "}
  88. {t("labels.addWatermark")}
  89. </label>
  90. ),
  91. });
  92. export const actionSaveScene = register({
  93. name: "saveScene",
  94. perform: async (elements, appState, value) => {
  95. try {
  96. const { fileHandle } = await saveAsJSON(elements, appState);
  97. return { commitToHistory: false, appState: { ...appState, fileHandle } };
  98. } catch (error) {
  99. if (error?.name !== "AbortError") {
  100. console.error(error);
  101. }
  102. return { commitToHistory: false };
  103. }
  104. },
  105. keyTest: (event) =>
  106. event.key === KEYS.S && event[KEYS.CTRL_OR_CMD] && !event.shiftKey,
  107. PanelComponent: ({ updateData }) => (
  108. <ToolButton
  109. type="button"
  110. icon={save}
  111. title={t("buttons.save")}
  112. aria-label={t("buttons.save")}
  113. showAriaLabel={useIsMobile()}
  114. onClick={() => updateData(null)}
  115. />
  116. ),
  117. });
  118. export const actionSaveAsScene = register({
  119. name: "saveAsScene",
  120. perform: async (elements, appState, value) => {
  121. try {
  122. const { fileHandle } = await saveAsJSON(elements, {
  123. ...appState,
  124. fileHandle: null,
  125. });
  126. return { commitToHistory: false, appState: { ...appState, fileHandle } };
  127. } catch (error) {
  128. if (error?.name !== "AbortError") {
  129. console.error(error);
  130. }
  131. return { commitToHistory: false };
  132. }
  133. },
  134. keyTest: (event) =>
  135. event.key === KEYS.S && event.shiftKey && event[KEYS.CTRL_OR_CMD],
  136. PanelComponent: ({ updateData }) => (
  137. <ToolButton
  138. type="button"
  139. icon={saveAs}
  140. title={t("buttons.saveAs")}
  141. aria-label={t("buttons.saveAs")}
  142. showAriaLabel={useIsMobile()}
  143. hidden={
  144. !("chooseFileSystemEntries" in window || "showOpenFilePicker" in window)
  145. }
  146. onClick={() => updateData(null)}
  147. />
  148. ),
  149. });
  150. export const actionLoadScene = register({
  151. name: "loadScene",
  152. perform: (
  153. elements,
  154. appState,
  155. { elements: loadedElements, appState: loadedAppState, error },
  156. ) => ({
  157. elements: loadedElements,
  158. appState: {
  159. ...loadedAppState,
  160. errorMessage: error,
  161. },
  162. commitToHistory: true,
  163. }),
  164. PanelComponent: ({ updateData, appState }) => (
  165. <ToolButton
  166. type="button"
  167. icon={load}
  168. title={t("buttons.load")}
  169. aria-label={t("buttons.load")}
  170. showAriaLabel={useIsMobile()}
  171. onClick={() => {
  172. loadFromJSON(appState)
  173. .then(({ elements, appState }) => {
  174. updateData({ elements, appState });
  175. })
  176. .catch(muteFSAbortError)
  177. .catch((error) => {
  178. updateData({ error: error.message });
  179. });
  180. }}
  181. />
  182. ),
  183. });