diff --git a/src/taskpane/components/AlignmentButtons.tsx b/src/taskpane/components/AlignmentButtons.tsx index 3a84298f..3479925d 100644 --- a/src/taskpane/components/AlignmentButtons.tsx +++ b/src/taskpane/components/AlignmentButtons.tsx @@ -1,3 +1,10 @@ +/** + * @file AlignmentButtons.tsx + * @description Component that provides functionality to align selected shapes + * in PowerPoint presentations. Supports horizontal alignment (left, center, right) + * and vertical alignment (top, middle, bottom). + */ + import * as React from "react"; import { Button, @@ -14,6 +21,35 @@ import { import { useStatusContext } from "./App"; import { useCommonStyles } from "./commonStyles"; +/** + * Configuration constants for alignment positions + */ +const ALIGNMENT_CONFIG = { + // Horizontal alignment positions + HORIZONTAL: { + LEFT: 81.1, + CENTER: 480, // Center point of slide + RIGHT: 879.75 + }, + // Vertical alignment positions + VERTICAL: { + TOP: 136.75, + MIDDLE: 321, // Middle point of slide + BOTTOM: 505.25 + }, + // Warning message when no shapes are selected + NO_SELECTION_WARNING: "No shapes are selected. Please select shapes first." +}; + +/** + * Alignment types for horizontal and vertical alignment + */ +type HorizontalAlignmentType = 'left' | 'center' | 'right'; +type VerticalAlignmentType = 'top' | 'middle' | 'bottom'; + +/** + * Component-specific styles + */ const useStyles = makeStyles({ alignmentGrid: { display: "grid", @@ -31,16 +67,41 @@ const useStyles = makeStyles({ } }); +/** + * AlignmentButtons component provides UI and functionality to align + * selected shapes in PowerPoint presentations. + * + * @returns React component + */ export const AlignmentButtons: React.FC = () => { const styles = useStyles(); const commonStyles = useCommonStyles(); const { - statusMessage, setStatusMessage, - statusType, setStatusType, - isProcessing, setIsProcessing + setStatusMessage, + setStatusType, + isProcessing, + setIsProcessing } = useStatusContext(); - const alignLeft = async () => { + /** + * Handles errors and updates the status message + * + * @param error - The error object + * @param operation - The operation being performed (for logging) + */ + const handleError = (error: any, operation: string): void => { + setStatusMessage(`Error: ${error.message || "Unknown error occurred"}`); + setStatusType("error"); + console.error(`${operation} error:`, error); + setIsProcessing(false); + }; + + /** + * Generic function to handle horizontal alignment of shapes + * + * @param alignmentType - The type of horizontal alignment to apply + */ + const handleHorizontalAlignment = async (alignmentType: HorizontalAlignmentType): Promise => { setIsProcessing(true); try { await PowerPoint.run(async (context) => { @@ -51,31 +112,45 @@ export const AlignmentButtons: React.FC = () => { // Check if shapes are selected if (shapes.items.length === 0) { - setStatusMessage("No shapes are selected. Please select shapes first."); + setStatusMessage(ALIGNMENT_CONFIG.NO_SELECTION_WARNING); setStatusType("warning"); return; } - // Set alignment for all shapes at once + // Set alignment for all shapes at once based on alignment type shapes.items.forEach(shape => { - shape.left = 81.1; + switch (alignmentType) { + case 'left': + shape.left = ALIGNMENT_CONFIG.HORIZONTAL.LEFT; + break; + case 'center': + shape.left = ALIGNMENT_CONFIG.HORIZONTAL.CENTER - (shape.width / 2); + break; + case 'right': + shape.left = ALIGNMENT_CONFIG.HORIZONTAL.RIGHT - shape.width; + break; + } }); + // Single sync after all updates await context.sync(); - setStatusMessage("Objects aligned to left."); + setStatusMessage(`Objects aligned to ${alignmentType}.`); setStatusType("success"); }); } catch (error) { - setStatusMessage(`Error: ${error.message}`); - setStatusType("error"); - console.error("Align left error:", error); + handleError(error, `Align ${alignmentType}`); } finally { setIsProcessing(false); } }; - const alignCenter = async () => { + /** + * Generic function to handle vertical alignment of shapes + * + * @param alignmentType - The type of vertical alignment to apply + */ + const handleVerticalAlignment = async (alignmentType: VerticalAlignmentType): Promise => { setIsProcessing(true); try { await PowerPoint.run(async (context) => { @@ -86,168 +161,79 @@ export const AlignmentButtons: React.FC = () => { // Check if shapes are selected if (shapes.items.length === 0) { - setStatusMessage("No shapes are selected. Please select shapes first."); + setStatusMessage(ALIGNMENT_CONFIG.NO_SELECTION_WARNING); setStatusType("warning"); return; } - // Set alignment for all shapes at once + // Set alignment for all shapes at once based on alignment type shapes.items.forEach(shape => { - shape.left = 480 - (shape.width / 2); + switch (alignmentType) { + case 'top': + shape.top = ALIGNMENT_CONFIG.VERTICAL.TOP; + break; + case 'middle': + shape.top = ALIGNMENT_CONFIG.VERTICAL.MIDDLE - (shape.height / 2); + break; + case 'bottom': + shape.top = ALIGNMENT_CONFIG.VERTICAL.BOTTOM - shape.height; + break; + } }); + // Single sync after all updates await context.sync(); - - setStatusMessage("Objects aligned to center."); + + setStatusMessage(`Objects aligned to ${alignmentType}.`); setStatusType("success"); }); } catch (error) { - setStatusMessage(`Error: ${error.message}`); - setStatusType("error"); - console.error("Align center error:", error); + handleError(error, `Align ${alignmentType}`); } finally { setIsProcessing(false); } }; - const alignRight = async () => { - setIsProcessing(true); - try { - await PowerPoint.run(async (context) => { - // Get the selected shapes - const shapes = context.presentation.getSelectedShapes(); - shapes.load("items"); - await context.sync(); - - // Check if shapes are selected - if (shapes.items.length === 0) { - setStatusMessage("No shapes are selected. Please select shapes first."); - setStatusType("warning"); - return; - } - - // Set alignment for all shapes at once - shapes.items.forEach(shape => { - shape.left = 879.75 - shape.width; - }); - // Single sync after all updates - await context.sync(); - - setStatusMessage("Objects aligned to right."); - setStatusType("success"); - }); - } catch (error) { - setStatusMessage(`Error: ${error.message}`); - setStatusType("error"); - console.error("Align right error:", error); - } finally { - setIsProcessing(false); - } + /** + * Aligns selected shapes to the left edge of the slide + */ + const alignLeft = async (): Promise => { + await handleHorizontalAlignment('left'); }; - const alignTop = async () => { - setIsProcessing(true); - try { - await PowerPoint.run(async (context) => { - // Get the selected shapes - const shapes = context.presentation.getSelectedShapes(); - shapes.load("items"); - await context.sync(); - - // Check if shapes are selected - if (shapes.items.length === 0) { - setStatusMessage("No shapes are selected. Please select shapes first."); - setStatusType("warning"); - return; - } - - // Set alignment for all shapes at once - shapes.items.forEach(shape => { - shape.top = 136.75; - }); - // Single sync after all updates - await context.sync(); - - setStatusMessage("Objects aligned to top."); - setStatusType("success"); - }); - } catch (error) { - setStatusMessage(`Error: ${error.message}`); - setStatusType("error"); - console.error("Align top error:", error); - } finally { - setIsProcessing(false); - } + /** + * Aligns selected shapes to the horizontal center of the slide + */ + const alignCenter = async (): Promise => { + await handleHorizontalAlignment('center'); }; - const alignMiddle = async () => { - setIsProcessing(true); - try { - await PowerPoint.run(async (context) => { - // Get the selected shapes - const shapes = context.presentation.getSelectedShapes(); - shapes.load("items"); - await context.sync(); - - // Check if shapes are selected - if (shapes.items.length === 0) { - setStatusMessage("No shapes are selected. Please select shapes first."); - setStatusType("warning"); - return; - } - - // Set alignment for all shapes at once - shapes.items.forEach(shape => { - shape.top = 321 - (shape.height / 2); - }); - // Single sync after all updates - await context.sync(); - - setStatusMessage("Objects aligned to middle."); - setStatusType("success"); - }); - } catch (error) { - setStatusMessage(`Error: ${error.message}`); - setStatusType("error"); - console.error("Align middle error:", error); - } finally { - setIsProcessing(false); - } + /** + * Aligns selected shapes to the right edge of the slide + */ + const alignRight = async (): Promise => { + await handleHorizontalAlignment('right'); }; - const alignBottom = async () => { - setIsProcessing(true); - try { - await PowerPoint.run(async (context) => { - // Get the selected shapes - const shapes = context.presentation.getSelectedShapes(); - shapes.load("items"); - await context.sync(); + /** + * Aligns selected shapes to the top edge of the slide + */ + const alignTop = async (): Promise => { + await handleVerticalAlignment('top'); + }; - // Check if shapes are selected - if (shapes.items.length === 0) { - setStatusMessage("No shapes are selected. Please select shapes first."); - setStatusType("warning"); - return; - } + /** + * Aligns selected shapes to the vertical middle of the slide + */ + const alignMiddle = async (): Promise => { + await handleVerticalAlignment('middle'); + }; - // Set alignment for all shapes at once - shapes.items.forEach(shape => { - shape.top = 505.25 - shape.height; - }); - // Single sync after all updates - await context.sync(); - - setStatusMessage("Objects aligned to bottom."); - setStatusType("success"); - }); - } catch (error) { - setStatusMessage(`Error: ${error.message}`); - setStatusType("error"); - console.error("Align bottom error:", error); - } finally { - setIsProcessing(false); - } + /** + * Aligns selected shapes to the bottom edge of the slide + */ + const alignBottom = async (): Promise => { + await handleVerticalAlignment('bottom'); }; return ( @@ -318,4 +304,4 @@ export const AlignmentButtons: React.FC = () => { ); }; -export default AlignmentButtons; \ No newline at end of file +export default AlignmentButtons;