actionExport.tsx 4.9 KB

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