From dd2561adc84551786da8ff8cf27bf93312b3eba6 Mon Sep 17 00:00:00 2001 From: Heiko Joerg Schick Date: Sat, 8 Mar 2025 00:30:43 +0100 Subject: [PATCH] Now only using one status box --- src/taskpane/components/App.tsx | 138 +++++++++++++++++--- src/taskpane/components/MatchProperties.tsx | 54 ++------ src/taskpane/components/MatchSizes.tsx | 54 ++------ 3 files changed, 139 insertions(+), 107 deletions(-) diff --git a/src/taskpane/components/App.tsx b/src/taskpane/components/App.tsx index de13c589..10f009ae 100644 --- a/src/taskpane/components/App.tsx +++ b/src/taskpane/components/App.tsx @@ -2,12 +2,34 @@ import * as React from "react"; import { useEffect, useState } from "react"; import MatchSizes from "./MatchSizes"; import MatchProperties from "./MatchProperties"; -import { makeStyles, Text, Subtitle1, tokens, Theme } from "@fluentui/react-components"; +import { makeStyles, Text, Subtitle1, tokens, Theme, Spinner } from "@fluentui/react-components"; +import { ShapeUnionRegular, SquareRegular, InfoRegular } from "@fluentui/react-icons"; interface AppProps { title: string; } +export type StatusType = "info" | "success" | "warning" | "error"; + +export interface StatusContextType { + statusMessage: string; + setStatusMessage: (message: string) => void; + statusType: StatusType; + setStatusType: (type: StatusType) => void; + isProcessing: boolean; + setIsProcessing: (processing: boolean) => void; +} + +export const StatusContext = React.createContext(undefined); + +export const useStatusContext = () => { + const context = React.useContext(StatusContext); + if (context === undefined) { + throw new Error('useStatusContext must be used within a StatusProvider'); + } + return context; +}; + const useStyles = makeStyles({ root: { display: "flex", @@ -18,8 +40,35 @@ const useStyles = makeStyles({ backgroundColor: tokens.colorNeutralBackground1, overflow: "auto", }, + statusContainer: { + marginTop: "4px", + padding: "8px 12px", + fontSize: "13px", + borderRadius: "6px", + backgroundColor: "#f3f2f1", // Light gray background + transition: "all 0.3s ease", + }, + successStatus: { + backgroundColor: "#DFF6DD", // Light green background + color: "#107C10", // Green text + }, + warningStatus: { + backgroundColor: "#FFF4CE", // Light yellow background + color: "#797673", // Dark gray text + }, + errorStatus: { + backgroundColor: "#FDE7E9", // Light red background + color: "#A80000", // Red text + }, + statusIcon: { + marginRight: "8px", + }, + statusText: { + display: "flex", + alignItems: "center", + }, section: { - marginBottom: "24px", + marginBottom: "4px", padding: "12px", borderRadius: "8px", backgroundColor: tokens.colorNeutralBackground2, @@ -47,6 +96,11 @@ const App: React.FC = () => { const styles = useStyles(); const [theme, setTheme] = useState("light"); + // Status state + const [statusMessage, setStatusMessage] = useState(""); + const [statusType, setStatusType] = useState("info"); + const [isProcessing, setIsProcessing] = useState(false); + // Check if macOS is in dark mode (would need to be expanded in production) useEffect(() => { // In a real implementation, we would listen to Office theme changes @@ -54,21 +108,75 @@ const App: React.FC = () => { setTheme(prefersDarkMode ? "dark" : "light"); }, []); + // Auto-clear success messages after 5 seconds + useEffect(() => { + let timer: number; + if (statusType === "success" && statusMessage) { + timer = window.setTimeout(() => { + setStatusMessage(""); + }, 5000); + } + return () => { + if (timer) clearTimeout(timer); + }; + }, [statusMessage, statusType]); + + const getStatusIcon = () => { + switch (statusType) { + case "success": + return ; + case "warning": + return ; + case "error": + return ; + default: + return null; + } + }; + return ( -
-
- - Shape Tools - - -
- + +
+
+ + Shape Tools + + +
+ +
+ + {/* Status message area at the bottom */} + {isProcessing && ( +
+
+ + Processing... +
+
+ )} + + {!isProcessing && statusMessage && ( +
+
+ {getStatusIcon()} + {statusMessage} +
+
+ )} + +
+ Edison • v1.0.0 +
- -
- Edison • v1.0.0 -
-
+ ); }; diff --git a/src/taskpane/components/MatchProperties.tsx b/src/taskpane/components/MatchProperties.tsx index 0155c38b..12dbf00f 100644 --- a/src/taskpane/components/MatchProperties.tsx +++ b/src/taskpane/components/MatchProperties.tsx @@ -2,16 +2,12 @@ import * as React from "react"; import { Button, makeStyles, - Text, - Spinner, tokens } from "@fluentui/react-components"; import { - ColorRegular, - SquareRegular, - ShapeUnionRegular, - InfoRegular + ColorRegular } from "@fluentui/react-icons"; +import { useStatusContext } from "./App"; const useStyles = makeStyles({ container: { @@ -62,9 +58,11 @@ const useStyles = makeStyles({ export const MatchProperties: React.FC = () => { const styles = useStyles(); - const [statusMessage, setStatusMessage] = React.useState(""); - const [statusType, setStatusType] = React.useState<"info" | "success" | "warning" | "error">("info"); - const [isProcessing, setIsProcessing] = React.useState(false); + const { + statusMessage, setStatusMessage, + statusType, setStatusType, + isProcessing, setIsProcessing + } = useStatusContext(); const matchPropertiesToFirstSelected = async () => { setIsProcessing(true); @@ -245,12 +243,7 @@ export const MatchProperties: React.FC = () => { setStatusType("error"); } - // Auto-clear success message after 5 seconds - setTimeout(() => { - if (statusType === "success") { - setStatusMessage(""); - } - }, 5000); + // Timeout is handled in App.tsx now }); } catch (error) { setStatusMessage(`Error: ${error.message}`); @@ -261,19 +254,6 @@ export const MatchProperties: React.FC = () => { } }; - const getStatusIcon = () => { - switch (statusType) { - case "success": - return ; - case "warning": - return ; - case "error": - return ; - default: - return null; - } - }; - return (
@@ -287,24 +267,6 @@ export const MatchProperties: React.FC = () => { Match Properties
- - {isProcessing && ( -
-
- - Applying properties... -
-
- )} - - {!isProcessing && statusMessage && ( -
-
- {getStatusIcon()} - {statusMessage} -
-
- )}
); }; diff --git a/src/taskpane/components/MatchSizes.tsx b/src/taskpane/components/MatchSizes.tsx index 8323a5ef..824de054 100644 --- a/src/taskpane/components/MatchSizes.tsx +++ b/src/taskpane/components/MatchSizes.tsx @@ -5,18 +5,15 @@ import { Text, Tooltip, InfoLabel, - Spinner, tokens, Card } from "@fluentui/react-components"; import { ArrowFitInRegular, ArrowSortDownRegular, - ArrowSortUpRegular, - SquareRegular, - ShapeUnionRegular, - InfoRegular + ArrowSortUpRegular } from "@fluentui/react-icons"; +import { useStatusContext } from "./App"; const useStyles = makeStyles({ container: { @@ -67,9 +64,11 @@ const useStyles = makeStyles({ export const MatchSizes: React.FC = () => { const styles = useStyles(); - const [statusMessage, setStatusMessage] = React.useState(""); - const [statusType, setStatusType] = React.useState<"info" | "success" | "warning" | "error">("info"); - const [isProcessing, setIsProcessing] = React.useState(false); + const { + statusMessage, setStatusMessage, + statusType, setStatusType, + isProcessing, setIsProcessing + } = useStatusContext(); const matchSizeToFirstSelected = async () => { setIsProcessing(true); @@ -109,12 +108,7 @@ export const MatchSizes: React.FC = () => { setStatusMessage(`Resized ${shapes.items.length - 1} shapes to match the first selected shape.`); setStatusType("success"); - // Auto-clear success message after 5 seconds - setTimeout(() => { - if (statusType === "success") { - setStatusMessage(""); - } - }, 5000); + // Timeout is handled in App.tsx now }); } catch (error) { setStatusMessage(`Error: ${error.message}`); @@ -126,19 +120,6 @@ export const MatchSizes: React.FC = () => { }; - const getStatusIcon = () => { - switch (statusType) { - case "success": - return ; - case "warning": - return ; - case "error": - return ; - default: - return null; - } - }; - return (
@@ -152,25 +133,6 @@ export const MatchSizes: React.FC = () => { Match Sizes
- - {isProcessing && ( -
-
- - Resizing shapes... -
-
- )} - - {!isProcessing && statusMessage && ( -
-
- {getStatusIcon()} - {statusMessage} -
-
- )} -
); };