Re-organisation of Draft and Confidential code

This commit is contained in:
2025-03-09 15:14:43 +01:00
parent 2c23c9adfc
commit 09ff4a6555
4 changed files with 603 additions and 10 deletions
@@ -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 (
<div className={commonStyles.container}>
<div className={styles.alignmentGrid}>
<Button
appearance="primary"
className={styles.alignButton}
onClick={alignLeft}
icon={<TextAlignLeftRegular />}
disabled={isProcessing}
title="Align Left"
>
Left
</Button>
<Button
appearance="primary"
className={styles.alignButton}
onClick={alignCenter}
icon={<TextAlignCenterRegular />}
disabled={isProcessing}
title="Align Center"
>
Center
</Button>
<Button
appearance="primary"
className={styles.alignButton}
onClick={alignRight}
icon={<TextAlignRightRegular />}
disabled={isProcessing}
title="Align Right"
>
Right
</Button>
<Button
appearance="primary"
className={styles.alignButton}
onClick={alignTop}
icon={<TextboxAlignTopRegular />}
disabled={isProcessing}
title="Align Top"
>
Top
</Button>
<Button
appearance="primary"
className={styles.alignButton}
onClick={alignMiddle}
icon={<TextboxAlignMiddleRegular />}
disabled={isProcessing}
title="Align Middle"
>
Middle
</Button>
<Button
appearance="primary"
className={styles.alignButton}
onClick={alignBottom}
icon={<TextboxAlignBottomRegular />}
disabled={isProcessing}
title="Align Bottom"
>
Bottom
</Button>
</div>
</div>
);
};
export default AlignmentButtons;
+4 -10
View File
@@ -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<AppProps> = () => {
<Subtitle1 block className={styles.sectionTitle}>
Watermarks
</Subtitle1>
<AddConfidential />
<ConfidentialButtons />
<div style={{ marginTop: "8px" }}></div>
<RemoveConfidential />
<div style={{ marginTop: "8px" }}></div>
<AddDraft />
<div style={{ marginTop: "8px" }}></div>
<RemoveDraft />
<DraftButtons />
</div>
<div className={styles.section}>
@@ -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 (
<div className={commonStyles.container}>
<div className={styles.buttonGrid}>
<Button
appearance="primary"
className={styles.confidentialButton}
onClick={addConfidentialMarking}
icon={<ShieldLockRegular />}
disabled={isProcessing}
title="Add Confidential"
>
Add Confidential
</Button>
<Button
appearance="primary"
className={styles.confidentialButton}
onClick={removeConfidentialMarking}
icon={<DismissRegular />}
disabled={isProcessing}
title="Remove Confidential"
>
Remove Confidential
</Button>
</div>
</div>
);
};
export default ConfidentialButtons;
+103
View File
@@ -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 (
<div className={commonStyles.container}>
<div className={styles.buttonGrid}>
<Button
appearance="primary"
className={styles.draftButton}
onClick={addDraftWatermark}
icon={<Edit24Regular />}
disabled={isProcessing}
title="Add Draft"
>
Add Draft
</Button>
<Button
appearance="primary"
className={styles.draftButton}
onClick={removeDraftWatermark}
icon={<DismissRegular />}
disabled={isProcessing}
title="Remove Draft"
>
Remove Draft
</Button>
</div>
</div>
);
};
export default DraftButtons;