From 09ff4a65559c2fae3ddfec57f64c72777f2016aa Mon Sep 17 00:00:00 2001 From: Heiko Joerg Schick Date: Sun, 9 Mar 2025 15:14:43 +0100 Subject: [PATCH] Re-organisation of Draft and Confidential code --- src/taskpane/components/AlignmentButtons.tsx | 219 ++++++++++++++ src/taskpane/components/App.tsx | 14 +- .../components/ConfidentialButtons.tsx | 277 ++++++++++++++++++ src/taskpane/components/DraftButtons.tsx | 103 +++++++ 4 files changed, 603 insertions(+), 10 deletions(-) create mode 100644 src/taskpane/components/AlignmentButtons.tsx create mode 100644 src/taskpane/components/ConfidentialButtons.tsx create mode 100644 src/taskpane/components/DraftButtons.tsx diff --git a/src/taskpane/components/AlignmentButtons.tsx b/src/taskpane/components/AlignmentButtons.tsx new file mode 100644 index 00000000..6d8a8fde --- /dev/null +++ b/src/taskpane/components/AlignmentButtons.tsx @@ -0,0 +1,219 @@ +import * as React from "react"; +import { + Button, + makeStyles +} from "@fluentui/react-components"; +import { + TextAlignLeftRegular, + TextAlignCenterRegular, + TextAlignRightRegular, + TextboxAlignTopRegular, + TextboxAlignMiddleRegular, + TextboxAlignBottomRegular +} from "@fluentui/react-icons"; +import { useStatusContext } from "./App"; +import { useCommonStyles } from "./commonStyles"; + +const useStyles = makeStyles({ + alignmentGrid: { + display: "grid", + gridTemplateColumns: "repeat(3, 1fr)", + gap: "8px", + width: "100%" + }, + alignButton: { + width: "100%", + minWidth: 0, + padding: "8px 4px", + "& span": { + justifyContent: "center" + } + } +}); + +export const AlignmentButtons: React.FC = () => { + const styles = useStyles(); + const commonStyles = useCommonStyles(); + const { + statusMessage, setStatusMessage, + statusType, setStatusType, + isProcessing, setIsProcessing + } = useStatusContext(); + + const alignLeft = async () => { + setIsProcessing(true); + try { + await PowerPoint.run(async (context) => { + const shapes = context.presentation.getSelectedShapes(); + // Implementation will go here + setStatusMessage("Objects aligned to left."); + setStatusType("success"); + }); + } catch (error) { + setStatusMessage(`Error: ${error.message}`); + setStatusType("error"); + console.error("Align left error:", error); + } finally { + setIsProcessing(false); + } + }; + + const alignCenter = async () => { + setIsProcessing(true); + try { + await PowerPoint.run(async (context) => { + const shapes = context.presentation.getSelectedShapes(); + // Implementation will go here + setStatusMessage("Objects aligned to center."); + setStatusType("success"); + }); + } catch (error) { + setStatusMessage(`Error: ${error.message}`); + setStatusType("error"); + console.error("Align center error:", error); + } finally { + setIsProcessing(false); + } + }; + + const alignRight = async () => { + setIsProcessing(true); + try { + await PowerPoint.run(async (context) => { + const shapes = context.presentation.getSelectedShapes(); + // Implementation will go here + setStatusMessage("Objects aligned to right."); + setStatusType("success"); + }); + } catch (error) { + setStatusMessage(`Error: ${error.message}`); + setStatusType("error"); + console.error("Align right error:", error); + } finally { + setIsProcessing(false); + } + }; + + const alignTop = async () => { + setIsProcessing(true); + try { + await PowerPoint.run(async (context) => { + const shapes = context.presentation.getSelectedShapes(); + // Implementation will go here + setStatusMessage("Objects aligned to top."); + setStatusType("success"); + }); + } catch (error) { + setStatusMessage(`Error: ${error.message}`); + setStatusType("error"); + console.error("Align top error:", error); + } finally { + setIsProcessing(false); + } + }; + + const alignMiddle = async () => { + setIsProcessing(true); + try { + await PowerPoint.run(async (context) => { + const shapes = context.presentation.getSelectedShapes(); + // Implementation will go here + setStatusMessage("Objects aligned to middle."); + setStatusType("success"); + }); + } catch (error) { + setStatusMessage(`Error: ${error.message}`); + setStatusType("error"); + console.error("Align middle error:", error); + } finally { + setIsProcessing(false); + } + }; + + const alignBottom = async () => { + setIsProcessing(true); + try { + await PowerPoint.run(async (context) => { + const shapes = context.presentation.getSelectedShapes(); + // Implementation will go here + setStatusMessage("Objects aligned to bottom."); + setStatusType("success"); + }); + } catch (error) { + setStatusMessage(`Error: ${error.message}`); + setStatusType("error"); + console.error("Align bottom error:", error); + } finally { + setIsProcessing(false); + } + }; + + return ( +
+
+ + + + + + +
+
+ ); +}; + +export default AlignmentButtons; \ No newline at end of file diff --git a/src/taskpane/components/App.tsx b/src/taskpane/components/App.tsx index 76225778..1b2cdcc5 100644 --- a/src/taskpane/components/App.tsx +++ b/src/taskpane/components/App.tsx @@ -5,10 +5,8 @@ import MatchProperties from "./MatchProperties"; import RoundImage from "./RoundImage"; import SwapPositions from "./SwapPositions"; import InsertTitles from "./InsertTitles"; -import AddConfidential from "./AddConfidential"; -import RemoveConfidential from "./RemoveConfidential"; -import AddDraft from "./AddDraft"; -import RemoveDraft from "./RemoveDraft"; +import ConfidentialButtons from "./ConfidentialButtons"; +import DraftButtons from "./DraftButtons"; import AddProgressBar from "./AddProgressBar"; import RemoveProgressBar from "./RemoveProgressBar"; import AlignmentButtons from "./AlignmentButtons"; @@ -182,13 +180,9 @@ const App: React.FC = () => { Watermarks - +
- -
- -
- +
diff --git a/src/taskpane/components/ConfidentialButtons.tsx b/src/taskpane/components/ConfidentialButtons.tsx new file mode 100644 index 00000000..748dc747 --- /dev/null +++ b/src/taskpane/components/ConfidentialButtons.tsx @@ -0,0 +1,277 @@ +import * as React from "react"; +import { + Button, + makeStyles +} from "@fluentui/react-components"; +import { + ShieldLockRegular, + DismissRegular +} from "@fluentui/react-icons"; +import { useStatusContext } from "./App"; +import { useCommonStyles } from "./commonStyles"; + +const useStyles = makeStyles({ + buttonGrid: { + display: "grid", + gridTemplateColumns: "repeat(2, 1fr)", + gap: "8px", + width: "100%" + }, + confidentialButton: { + width: "100%", + minWidth: 0, + padding: "8px 4px", + "& span": { + justifyContent: "center" + } + } +}); + +export const ConfidentialButtons: React.FC = () => { + const styles = useStyles(); + const commonStyles = useCommonStyles(); + const { + statusMessage, setStatusMessage, + statusType, setStatusType, + isProcessing, setIsProcessing + } = useStatusContext(); + + const addConfidentialMarking = async () => { + setIsProcessing(true); + try { + await PowerPoint.run(async (context) => { + try { + // Get all slides in the presentation + const slides = context.presentation.slides; + slides.load("items"); + await context.sync(); + + // Get a reference slide to determine dimensions + // Since we can't access presentation width/height directly + if (slides.items.length === 0) { + setStatusMessage("No slides found in the presentation."); + setStatusType("warning"); + return; + } + + // Get dimensions from the first slide + const firstSlide = slides.items[0]; + + // Standard PowerPoint slide dimensions (in points) + // We'll use these as fallbacks and adjust if we can get actual dimensions + let slideWidth = 960; // Default slide width (10 inches * 72 points) + let slideHeight = 540; // Default slide height (7.5 inches * 72 points) + + // Process counter + let processedSlides = 0; + let errorSlides = 0; + + // Process each slide + for (let i = 0; i < slides.items.length; i++) { + try { + const slide = slides.items[i]; + + const positionFromTop = slideHeight - 23; + + // Create the textbox - make sure it spans the full width of the slide + // This is important for proper centering + const textBox = slide.shapes.addTextBox(""); + + // Make it span most of the width of the slide (90% centered) + // This ensures we have room for the text to be centered + const textBoxWidth = slideWidth * 1; // 0.9 + textBox.left = (slideWidth - textBoxWidth) / 2; // Center it horizontally + textBox.top = positionFromTop; + textBox.width = textBoxWidth; + textBox.height = 25; // Smaller height for footer text + + console.log("Starting PowerPoint add-in execution."); + console.log(textBox.left); + console.log(textBox.top); + console.log(textBox.width); + console.log(textBox.height); + await context.sync(); + + // Load textFrame to set text properties + textBox.load("textFrame"); + await context.sync(); + + if (textBox.textFrame) { + // Load textRange to set text and properties + textBox.textFrame.load("textRange"); + await context.sync(); + + // Need to load font and paragraphFormat + textBox.textFrame.textRange.load("font,paragraphFormat"); + await context.sync(); + + // Set font properties + try { + // Ensure the font is loaded properly before setting properties + textBox.textFrame.textRange.font.load(); + await context.sync(); + + // Set font properties exactly as in the VBA code + textBox.textFrame.textRange.font.name = "Inter"; + textBox.textFrame.textRange.font.size = 8; + textBox.textFrame.textRange.paragraphFormat.horizontalAlignment = "Center" + + // Set the color to RGB(218, 19, 53) + // Different APIs may need different color formats + textBox.textFrame.textRange.font.color = "#DA1335"; + + // Set the text + textBox.textFrame.textRange.text = "– Confidential –"; + } catch (fontError) { + console.error("Error setting font properties:", fontError); + // Even if we can't set the font properties exactly, continue with default font + } + + // Add a name/tag to the shape for identification + textBox.name = "ConfidentialMarking"; + + await context.sync(); + processedSlides++; + } + } catch (slideError) { + console.error(`Error processing slide ${i+1}:`, slideError); + errorSlides++; + // Continue to the next slide + continue; + } + } + + // Report results + if (processedSlides > 0) { + setStatusMessage(`Added confidential marking to ${processedSlides} slides.`); + setStatusType("success"); + } else if (errorSlides > 0) { + setStatusMessage(`Failed to add markings. Errors on ${errorSlides} slides.`); + setStatusType("error"); + } else { + setStatusMessage("No slides found to add confidential marking."); + setStatusType("warning"); + } + + } catch (innerError) { + console.error("Inner error:", innerError); + throw innerError; // Re-throw to outer catch + } + }); + } catch (error) { + setStatusMessage(`Error: ${error.message}`); + setStatusType("error"); + console.error("Add confidential error:", error); + } finally { + setIsProcessing(false); + } + }; + + const removeConfidentialMarking = async () => { + setIsProcessing(true); + try { + await PowerPoint.run(async (context) => { + try { + // Get all slides in the presentation + const slides = context.presentation.slides; + slides.load("items"); + await context.sync(); + + if (slides.items.length === 0) { + setStatusMessage("No slides found in the presentation."); + setStatusType("warning"); + return; + } + + // Process counter + let processedSlides = 0; + let errorSlides = 0; + let removedCount = 0; + + // Process each slide + for (let i = 0; i < slides.items.length; i++) { + try { + const slide = slides.items[i]; + + // Load all shapes on the slide + slide.shapes.load("items"); + await context.sync(); + + // Find shapes with name "ConfidentialMarking" + for (let j = 0; j < slide.shapes.items.length; j++) { + const shape = slide.shapes.items[j]; + shape.load("name"); + await context.sync(); + + if (shape.name === "ConfidentialMarking") { + // Delete the confidential marking shape + shape.delete(); + removedCount++; + } + } + + await context.sync(); + processedSlides++; + } catch (slideError) { + console.error(`Error processing slide ${i+1}:`, slideError); + errorSlides++; + // Continue to the next slide + continue; + } + } + + // Report results + if (removedCount > 0) { + setStatusMessage(`Removed ${removedCount} confidential markings from ${processedSlides} slides.`); + setStatusType("success"); + } else if (errorSlides > 0) { + setStatusMessage(`Failed to remove markings. Errors on ${errorSlides} slides.`); + setStatusType("error"); + } else { + setStatusMessage("No confidential markings found to remove."); + setStatusType("info"); + } + + } catch (innerError) { + console.error("Inner error:", innerError); + throw innerError; // Re-throw to outer catch + } + }); + } catch (error) { + setStatusMessage(`Error: ${error.message}`); + setStatusType("error"); + console.error("Remove confidential error:", error); + } finally { + setIsProcessing(false); + } + }; + + return ( +
+
+ + +
+
+ ); +}; + +export default ConfidentialButtons; \ No newline at end of file diff --git a/src/taskpane/components/DraftButtons.tsx b/src/taskpane/components/DraftButtons.tsx new file mode 100644 index 00000000..313994ab --- /dev/null +++ b/src/taskpane/components/DraftButtons.tsx @@ -0,0 +1,103 @@ +import * as React from "react"; +import { + Button, + makeStyles +} from "@fluentui/react-components"; +import { + Edit24Regular, + DismissRegular +} from "@fluentui/react-icons"; +import { useStatusContext } from "./App"; +import { useCommonStyles } from "./commonStyles"; + +const useStyles = makeStyles({ + buttonGrid: { + display: "grid", + gridTemplateColumns: "repeat(2, 1fr)", + gap: "8px", + width: "100%" + }, + draftButton: { + width: "100%", + minWidth: 0, + padding: "8px 4px", + "& span": { + justifyContent: "center" + } + } +}); + +export const DraftButtons: React.FC = () => { + const styles = useStyles(); + const commonStyles = useCommonStyles(); + const { + statusMessage, setStatusMessage, + statusType, setStatusType, + isProcessing, setIsProcessing + } = useStatusContext(); + + const addDraftWatermark = async () => { + setIsProcessing(true); + try { + await PowerPoint.run(async (context) => { + const shapes = context.presentation.getSelectedShapes(); + // Implementation will go here + setStatusMessage("Added draft watermark to slides."); + setStatusType("success"); + }); + } catch (error) { + setStatusMessage(`Error: ${error.message}`); + setStatusType("error"); + console.error("Add draft watermark error:", error); + } finally { + setIsProcessing(false); + } + }; + + const removeDraftWatermark = async () => { + setIsProcessing(true); + try { + await PowerPoint.run(async (context) => { + const shapes = context.presentation.getSelectedShapes(); + // Implementation will go here + setStatusMessage("Removed draft watermarks from slides."); + setStatusType("success"); + }); + } catch (error) { + setStatusMessage(`Error: ${error.message}`); + setStatusType("error"); + console.error("Remove draft watermark error:", error); + } finally { + setIsProcessing(false); + } + }; + + return ( +
+
+ + +
+
+ ); +}; + +export default DraftButtons; \ No newline at end of file