Initial version of add and remove progress bar.
This commit is contained in:
@@ -1,136 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import {
|
|
||||||
Button
|
|
||||||
} from "@fluentui/react-components";
|
|
||||||
import {
|
|
||||||
GaugeRegular
|
|
||||||
} from "@fluentui/react-icons";
|
|
||||||
import { useStatusContext } from "./App";
|
|
||||||
import { useCommonStyles } from "./commonStyles";
|
|
||||||
|
|
||||||
export const AddProgressBar: React.FC = () => {
|
|
||||||
const styles = 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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.container}>
|
|
||||||
<div className={styles.buttonGroup}>
|
|
||||||
<Button
|
|
||||||
appearance="primary"
|
|
||||||
className={styles.actionButton}
|
|
||||||
onClick={addProgressBar}
|
|
||||||
icon={<GaugeRegular />}
|
|
||||||
disabled={isProcessing}
|
|
||||||
>
|
|
||||||
Add Progress Bar
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AddProgressBar;
|
|
||||||
@@ -7,8 +7,7 @@ import SwapPositions from "./SwapPositions";
|
|||||||
import InsertTitles from "./InsertTitles";
|
import InsertTitles from "./InsertTitles";
|
||||||
import ConfidentialButtons from "./ConfidentialButtons";
|
import ConfidentialButtons from "./ConfidentialButtons";
|
||||||
import DraftButtons from "./DraftButtons";
|
import DraftButtons from "./DraftButtons";
|
||||||
import AddProgressBar from "./AddProgressBar";
|
import ProgressBarButtons from "./ProgressBarButtons";
|
||||||
import RemoveProgressBar from "./RemoveProgressBar";
|
|
||||||
import AlignmentButtons from "./AlignmentButtons";
|
import AlignmentButtons from "./AlignmentButtons";
|
||||||
import { makeStyles, Text, Subtitle1, tokens, Theme, Spinner } from "@fluentui/react-components";
|
import { makeStyles, Text, Subtitle1, tokens, Theme, Spinner } from "@fluentui/react-components";
|
||||||
import { ShapeUnionRegular, SquareRegular, InfoRegular } from "@fluentui/react-icons";
|
import { ShapeUnionRegular, SquareRegular, InfoRegular } from "@fluentui/react-icons";
|
||||||
@@ -171,9 +170,7 @@ const App: React.FC<AppProps> = () => {
|
|||||||
<Subtitle1 block className={styles.sectionTitle}>
|
<Subtitle1 block className={styles.sectionTitle}>
|
||||||
Progress Bar
|
Progress Bar
|
||||||
</Subtitle1>
|
</Subtitle1>
|
||||||
<AddProgressBar />
|
<ProgressBarButtons />
|
||||||
<div style={{ marginTop: "8px" }}></div>
|
|
||||||
<RemoveProgressBar />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.section}>
|
<div className={styles.section}>
|
||||||
|
|||||||
@@ -1,96 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import {
|
|
||||||
Button
|
|
||||||
} from "@fluentui/react-components";
|
|
||||||
import {
|
|
||||||
DismissRegular
|
|
||||||
} from "@fluentui/react-icons";
|
|
||||||
import { useStatusContext } from "./App";
|
|
||||||
import { useCommonStyles } from "./commonStyles";
|
|
||||||
|
|
||||||
export const RemoveProgressBar: React.FC = () => {
|
|
||||||
const styles = useCommonStyles();
|
|
||||||
const {
|
|
||||||
statusMessage, setStatusMessage,
|
|
||||||
statusType, setStatusType,
|
|
||||||
isProcessing, setIsProcessing
|
|
||||||
} = useStatusContext();
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<div className={styles.container}>
|
|
||||||
<div className={styles.buttonGroup}>
|
|
||||||
<Button
|
|
||||||
appearance="primary"
|
|
||||||
className={styles.actionButton}
|
|
||||||
onClick={removeProgressBar}
|
|
||||||
icon={<DismissRegular />}
|
|
||||||
disabled={isProcessing}
|
|
||||||
>
|
|
||||||
Remove Progress Bar
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RemoveProgressBar;
|
|
||||||
Reference in New Issue
Block a user