diff --git a/src/taskpane/components/ProgressBarButtons.tsx b/src/taskpane/components/ProgressBarButtons.tsx new file mode 100644 index 00000000..43abc333 --- /dev/null +++ b/src/taskpane/components/ProgressBarButtons.tsx @@ -0,0 +1,227 @@ +import * as React from "react"; +import { + Button, + makeStyles +} from "@fluentui/react-components"; +import { + GaugeRegular, + 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%" + }, + progressButton: { + width: "100%", + minWidth: 0, + padding: "8px 4px", + "& span": { + justifyContent: "center" + } + } +}); + +export const ProgressBarButtons: React.FC = () => { + const styles = useStyles(); + const commonStyles = useCommonStyles(); + const { + statusMessage, setStatusMessage, + statusType, setStatusType, + isProcessing, setIsProcessing + } = useStatusContext(); + + const addProgressBar = async () => { + setIsProcessing(true); + try { + await PowerPoint.run(async (context) => { + // Get all slides + const presentation = context.presentation; + const slides = presentation.slides; + slides.load("items"); + + // Need to load the first slide to get access to dimensions + await context.sync(); + + if (slides.items.length === 0) { + throw new Error("No slides in the presentation"); + } + + // Get dimensions from first slide + const firstSlide = slides.items[0]; + + // We'll create our own width and height based on standard values + // since direct access to slide dimensions is tricky in Office.js + const slideWidth = 960; // Standard PowerPoint slide width in points + const slideHeight = 540; // Standard PowerPoint slide height in points + + // Define progress bar properties + const progressBarHeight = 1; // Height in points + const segmentGap = 0; // Gap between segments + const startY = slideHeight - 28; // 28 points from bottom + + // Load slides first to get count + await context.sync(); + + const slideCount = slides.items.length; + const segmentWidth = slideWidth / slideCount; + + // Process each slide + for (let i = 0; i < slideCount; i++) { + const slide = slides.items[i]; + + // Load slide properties to check layout + slide.load("layout"); + await context.sync(); + + const layoutName = slide.layout.name; + + // Check if this is a layout we want to add the progress bar to + // Note: Office.js might not have exact same layout names as VBA, + // so we're checking for common layouts + if (layoutName.includes("Title") || + layoutName.includes("Content") || + layoutName.includes("Two Content") || + layoutName.includes("Blank")) { + + // Add progress bar segments to this slide + for (let j = 0; j < slideCount; j++) { + // Create line for this segment + const startX = j * segmentWidth; + const endX = (j + 1) * segmentWidth - segmentGap; + + // Create a rectangle with minimal height to simulate a line + // since proper line creation is challenging with the Office JS API + const line = slide.shapes.addGeometricShape("Rectangle"); + + // Set the rectangle's position to look like a line + line.left = startX; + line.top = startY; + line.width = endX - startX; + line.height = progressBarHeight; // Very small height + + // Set fill color based on progress instead of line color + // We're using a rectangle, so fill is more appropriate than line + if (j <= i) { + // Blue for progressed segments (RGB 32, 81, 112) + line.fill.setSolidColor("#205170"); + } else { + // Grey for segments not yet progressed (RGB 242, 242, 242) + line.fill.setSolidColor("#F2F2F2"); + } + + // Remove the border/outline entirely + line.lineFormat.visible = false; + + // Since tags API might not be fully supported, use shape name as identifier + line.name = "progress_bar_" + j; + } + } + } + + setStatusMessage("Added progress bar to slides."); + setStatusType("success"); + }); + } catch (error) { + setStatusMessage(`Error: ${error.message}`); + setStatusType("error"); + console.error("Add progress bar error:", error); + } finally { + setIsProcessing(false); + } + }; + + const removeProgressBar = async () => { + setIsProcessing(true); + try { + await PowerPoint.run(async (context) => { + // Get all slides + const presentation = context.presentation; + const slides = presentation.slides; + slides.load("items"); + await context.sync(); + + let removedCount = 0; + + // Process each slide + for (let i = 0; i < slides.items.length; i++) { + const slide = slides.items[i]; + + // Load all shapes in this slide + const shapes = slide.shapes; + shapes.load("items"); + await context.sync(); + + // Find shapes with our tag + const progressBarShapes = []; + for (let j = 0; j < shapes.items.length; j++) { + const shape = shapes.items[j]; + // Check if this shape name indicates it's a progress bar + shape.load("name"); + await context.sync(); + + // Look for shapes with names starting with "progress_bar_" + if (shape.name && shape.name.startsWith("progress_bar_")) { + progressBarShapes.push(shape); + } + } + + // Delete all progress bar shapes + for (const shape of progressBarShapes) { + shape.delete(); + removedCount++; + } + } + + await context.sync(); + + if (removedCount > 0) { + setStatusMessage(`Removed ${removedCount} progress bar elements from slides.`); + } else { + setStatusMessage("No progress bar elements found to remove."); + } + setStatusType("success"); + }); + } catch (error) { + setStatusMessage(`Error: ${error.message}`); + setStatusType("error"); + console.error("Remove progress bar error:", error); + } finally { + setIsProcessing(false); + } + }; + + return ( +