Skip to content

Commit

Permalink
Add basic import button for theme
Browse files Browse the repository at this point in the history
  • Loading branch information
thsparks committed Mar 3, 2025
1 parent dc0ce28 commit 1e51578
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 1 deletion.
25 changes: 24 additions & 1 deletion themebuilder/src/components/ThemeEditorToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as auth from "../services/authClient";
import css from "./styling/ThemeEditor.module.scss";
import { AppStateContext } from "../state/appStateContext";
import { BaseThemePicker } from "./BaseThemePicker";
import { exportTheme } from "../services/fileSystemService";
import { exportTheme, importThemeFromFileAsync } from "../services/fileSystemService";
import { Button } from "react-common/components/controls/Button";
import { classList } from "react-common/components/util";
import { ThemeManager } from "react-common/components/theming/themeManager";
Expand All @@ -21,6 +21,7 @@ export const ThemeEditorToolbar = () => {
fgColor: "var(--pxt-neutral-foreground1)",
};
const { state } = React.useContext(AppStateContext);
const fileInputRef = React.useRef<HTMLInputElement>(null);
const { editingTheme } = state;
const [saveState, setSaveState] = React.useState<SaveState>(defaultSaveState);

Expand All @@ -36,6 +37,13 @@ export const ThemeEditorToolbar = () => {
exportTheme(editingTheme);
}

function handleImportFromFile(event: React.ChangeEvent<HTMLInputElement>) {
const file = event.target.files?.[0];
if (file) {
importThemeFromFileAsync(file);
}
}

async function handleSaveToProfileClicked() {
if (!editingTheme) return;

Expand Down Expand Up @@ -81,6 +89,21 @@ export const ThemeEditorToolbar = () => {
borderColor: saveState.fgColor,
}}
/>
{/* The button triggers a hidden file input to open the file browser */}
<Button
className={classList(css["import-button"], css["toolbar-icon-button"])}
title={lf("Import Theme From File")}
leftIcon="fas fa-file-import"
onClick={() => fileInputRef?.current?.click()}
/>
<input
type="file"
ref={fileInputRef}
className="hidden"
onChange={handleImportFromFile}
aria-label={lf("Select Theme From File")}
accept=".json"
/>
<Button
className={classList(css["save-button"], css["toolbar-icon-button"])}
leftIcon="fas fa-file-export"
Expand Down
15 changes: 15 additions & 0 deletions themebuilder/src/services/fileSystemService.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
// Serializes the given theme and writes it to a file.

import { setCurrentEditingTheme } from "../transforms/setCurrentEditingTheme";

// Returns true if the file was written successfully, false otherwise.
export function exportTheme(theme: pxt.ColorThemeInfo): boolean {
if (!theme?.id) return false;
Expand All @@ -16,3 +19,15 @@ export function exportTheme(theme: pxt.ColorThemeInfo): boolean {
return false;
}
}

export async function importThemeFromFileAsync(file: File): Promise<boolean> {
try {
const themeJson = await pxt.Util.fileReadAsTextAsync(file);
const theme = JSON.parse(themeJson) as pxt.ColorThemeInfo;
setCurrentEditingTheme(theme);
return true;
} catch (error) {
console.error("Unable to parse theme.", error);
return false;
}
}

0 comments on commit 1e51578

Please sign in to comment.