gridstack_batch_update
Toggle batch update mode to optimize performance when making multiple layout changes in GridStack dashboards.
Instructions
Enable/disable batch update mode for efficiency
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| flag | No | Enable (true) or disable (false) batch mode |
Implementation Reference
- src/tools/index.ts:379-392 (registration)Registration of the 'gridstack_batch_update' tool in the listTools method, including description and input schema.{ name: "gridstack_batch_update", description: "Enable/disable batch update mode for efficiency", inputSchema: { type: "object", properties: { flag: { type: "boolean", description: "Enable (true) or disable (false) batch mode", default: true, }, }, }, },
- src/tools/index.ts:992-999 (handler)The handler implementation that processes input parameters and generates the GridStack 'batchUpdate' code using the utility.private async batchUpdate(params: BatchUpdateParams): Promise<string> { const { flag = true } = params; return this.utils.generateGridStackCode("batchUpdate", { flag, code: `grid.batchUpdate(${flag});`, }); }
- src/types.ts:216-218 (schema)TypeScript interface defining the input parameters for the batchUpdate handler.export interface BatchUpdateParams { flag?: boolean; }
- src/tools/index.ts:801-802 (registration)Dispatch case in the central callTool switch statement that routes to the specific handler.case "gridstack_batch_update": return this.batchUpdate(args as BatchUpdateParams);
- src/utils/gridstack-utils.ts:16-496 (helper)Utility method used by handlers to generate standardized response code, descriptions, examples, and notes for GridStack operations.generateGridStackCode(operation: string, params: any): string { const result: GridStackCodeResult = { operation, parameters: params, code: params.code || "", description: this.getOperationDescription(operation), example: this.getOperationExample(operation), notes: this.getOperationNotes(operation), }; return this.formatResponse(result); } /** * Format GridStack options for display */ formatOptions(options: GridStackOptions): string { const formatted = JSON.stringify(options, null, 2); return formatted; } /** * Validate widget configuration */ validateWidget(widget: GridStackWidget): { valid: boolean; errors: string[]; } { const errors: string[] = []; // Check required properties for positioning if ( widget.x !== undefined && (widget.x < 0 || !Number.isInteger(widget.x)) ) { errors.push("x position must be a non-negative integer"); } if ( widget.y !== undefined && (widget.y < 0 || !Number.isInteger(widget.y)) ) { errors.push("y position must be a non-negative integer"); } if ( widget.w !== undefined && (widget.w <= 0 || !Number.isInteger(widget.w)) ) { errors.push("width must be a positive integer"); } if ( widget.h !== undefined && (widget.h <= 0 || !Number.isInteger(widget.h)) ) { errors.push("height must be a positive integer"); } // Check min/max constraints if ( widget.minW !== undefined && widget.maxW !== undefined && widget.minW > widget.maxW ) { errors.push("minW cannot be greater than maxW"); } if ( widget.minH !== undefined && widget.maxH !== undefined && widget.minH > widget.maxH ) { errors.push("minH cannot be greater than maxH"); } if ( widget.w !== undefined && widget.minW !== undefined && widget.w < widget.minW ) { errors.push("width cannot be less than minW"); } if ( widget.w !== undefined && widget.maxW !== undefined && widget.w > widget.maxW ) { errors.push("width cannot be greater than maxW"); } if ( widget.h !== undefined && widget.minH !== undefined && widget.h < widget.minH ) { errors.push("height cannot be less than minH"); } if ( widget.h !== undefined && widget.maxH !== undefined && widget.h > widget.maxH ) { errors.push("height cannot be greater than maxH"); } return { valid: errors.length === 0, errors, }; } /** * Generate CSS selector validation */ validateSelector(selector: string): { valid: boolean; error?: string } { if (!selector || typeof selector !== "string") { return { valid: false, error: "Selector must be a non-empty string" }; } // Basic CSS selector validation try { document.querySelector(selector); return { valid: true }; } catch (error) { return { valid: false, error: "Invalid CSS selector format" }; } } /** * Generate responsive breakpoint validation */ validateBreakpoints(breakpoints: any[]): { valid: boolean; errors: string[]; } { const errors: string[] = []; if (!Array.isArray(breakpoints)) { errors.push("Breakpoints must be an array"); return { valid: false, errors }; } breakpoints.forEach((bp, index) => { if (typeof bp !== "object" || bp === null) { errors.push(`Breakpoint at index ${index} must be an object`); return; } if (typeof bp.w !== "number" || bp.w <= 0) { errors.push( `Breakpoint at index ${index} must have a positive width (w)` ); } if (typeof bp.c !== "number" || bp.c <= 0 || !Number.isInteger(bp.c)) { errors.push( `Breakpoint at index ${index} must have a positive integer columns (c)` ); } }); // Check for duplicate widths const widths = breakpoints .map((bp) => bp.w) .filter((w) => typeof w === "number"); const uniqueWidths = new Set(widths); if (widths.length !== uniqueWidths.size) { errors.push("Breakpoints cannot have duplicate widths"); } return { valid: errors.length === 0, errors, }; } /** * Generate HTML template for GridStack */ generateHTMLTemplate(options: GridStackOptions = {}): string { const { class: className = "", column = 12, cellHeight = "auto", margin = 10, children = [], } = options; const gridClass = `grid-stack${className ? ` ${className}` : ""}`; let html = `<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>GridStack Layout</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gridstack@12/dist/gridstack.min.css"> <style> .grid-stack-item-content { background-color: #f8f9fa; border: 1px solid #dee2e6; border-radius: 4px; padding: 10px; } </style> </head> <body> <div class="${gridClass}">`; // Add children widgets children.forEach((widget) => { const attrs = [ `gs-x="${widget.x || 0}"`, `gs-y="${widget.y || 0}"`, `gs-w="${widget.w || 1}"`, `gs-h="${widget.h || 1}"`, ]; if (widget.id) attrs.push(`gs-id="${widget.id}"`); if (widget.minW) attrs.push(`gs-min-w="${widget.minW}"`); if (widget.maxW) attrs.push(`gs-max-w="${widget.maxW}"`); if (widget.minH) attrs.push(`gs-min-h="${widget.minH}"`); if (widget.maxH) attrs.push(`gs-max-h="${widget.maxH}"`); if (widget.locked) attrs.push(`gs-locked="true"`); if (widget.noResize) attrs.push(`gs-no-resize="true"`); if (widget.noMove) attrs.push(`gs-no-move="true"`); html += ` <div class="grid-stack-item" ${attrs.join(" ")}> <div class="grid-stack-item-content"> ${widget.content || `Widget ${widget.id || "Item"}`} </div> </div>`; }); html += ` </div> <script src="https://cdn.jsdelivr.net/npm/gridstack@12/dist/gridstack-all.js"></script> <script> const grid = GridStack.init(${JSON.stringify(options, null, 4)}); </script> </body> </html>`; return html; } /** * Generate CSS for GridStack customization */ generateCustomCSS( options: { cellHeight?: number | string; margin?: number | string; columns?: number; customColors?: { background?: string; border?: string; hover?: string; }; } = {} ): string { const { cellHeight, margin, columns = 12, customColors } = options; let css = `/* GridStack Custom Styles */\n`; // Grid container styles css += `.grid-stack {\n`; if (margin) { css += ` margin: ${ typeof margin === "number" ? `${margin}px` : margin };\n`; } css += `}\n\n`; // Cell height if (cellHeight) { css += `.grid-stack > .grid-stack-item > .grid-stack-item-content {\n`; css += ` min-height: ${ typeof cellHeight === "number" ? `${cellHeight}px` : cellHeight };\n`; css += `}\n\n`; } // Custom colors if (customColors) { css += `.grid-stack-item-content {\n`; if (customColors.background) { css += ` background-color: ${customColors.background};\n`; } if (customColors.border) { css += ` border-color: ${customColors.border};\n`; } css += `}\n\n`; if (customColors.hover) { css += `.grid-stack-item:hover .grid-stack-item-content {\n`; css += ` background-color: ${customColors.hover};\n`; css += `}\n\n`; } } // Responsive columns if (columns !== 12) { css += `/* Custom ${columns}-column grid */\n`; for (let i = 1; i <= columns; i++) { const width = ((100 / columns) * i).toFixed(6); css += `.grid-stack > .grid-stack-item[gs-w="${i}"] { width: ${width}%; }\n`; css += `.grid-stack > .grid-stack-item[gs-x="${i - 1}"] { left: ${( (100 / columns) * (i - 1) ).toFixed(6)}%; }\n`; } css += `\n`; } return css; } private getOperationDescription(operation: string): string { const descriptions: Record<string, string> = { init: "Initialize a new GridStack instance with the specified options", addWidget: "Add a new widget to the grid at the specified position", removeWidget: "Remove a widget from the grid", updateWidget: "Update widget properties (position, size, constraints)", moveWidget: "Move a widget to a new position", resizeWidget: "Resize a widget to new dimensions", compact: "Compact the grid layout to remove gaps", float: "Enable or disable floating widget mode", column: "Change the number of columns in the grid", cellHeight: "Update the height of grid cells", margin: "Update the margin/gap between grid items", batchUpdate: "Enable batch update mode for efficient multiple operations", save: "Serialize the current grid layout to JSON", load: "Load a grid layout from JSON data", enable: "Enable or disable grid interactions", destroy: "Destroy the grid instance and clean up", getGridItems: "Get all grid items (widgets)", setResponsive: "Configure responsive breakpoints", willItFit: "Check if a widget will fit at the specified position", isAreaEmpty: "Check if a grid area is empty", getCellHeight: "Get the current cell height", getCellFromPixel: "Convert pixel coordinates to grid cell position", addEventListener: "Add an event listener for grid events", removeEventListener: "Remove an event listener", makeWidget: "Convert an existing DOM element into a grid widget", removeAll: "Remove all widgets from the grid", getMargin: "Get current margin values", getColumn: "Get current number of columns", getFloat: "Get current float mode state", addGrid: "Create a new grid with options and children (static method)", }; return descriptions[operation] || "GridStack operation"; } private getOperationExample(operation: string): string { const examples: Record<string, string> = { init: `// Initialize with basic options const grid = GridStack.init({ column: 12, cellHeight: 'auto', margin: 10, float: false });`, addWidget: `// Add a widget with content grid.addWidget({ x: 0, y: 0, w: 3, h: 2, content: '<div>My Widget</div>', id: 'widget1' });`, removeWidget: `// Remove widget by selector grid.removeWidget('#widget1');`, updateWidget: `// Update widget properties grid.update('#widget1', { w: 4, h: 3, locked: true });`, moveWidget: `// Move widget to new position grid.move('#widget1', 2, 1);`, resizeWidget: `// Resize widget grid.resize('#widget1', 4, 3);`, save: `// Save layout to JSON const layout = grid.save(true); localStorage.setItem('layout', JSON.stringify(layout));`, load: `// Load layout from JSON const layout = JSON.parse(localStorage.getItem('layout')); grid.load(layout);`, }; return examples[operation] || `// ${operation} example`; } private getOperationNotes(operation: string): string[] { const notes: Record<string, string[]> = { init: [ "Grid container must have 'grid-stack' class", "Include GridStack CSS and JS files", "Options are merged with defaults", ], addWidget: [ "Widget will be auto-positioned if x,y not specified", "Triggers 'added' event by default", "Returns the created DOM element", ], removeWidget: [ "Can accept element, selector, or GridStackNode", "Set removeDOM=false to keep in DOM", "Triggers 'removed' event by default", ], batchUpdate: [ "Use before multiple operations for efficiency", "Call with false to end batch mode", "Only one 'change' event fired at end", ], save: [ "Returns array of widget configurations", "saveContent=true includes HTML content", "saveGridOpt=true includes grid options", ], load: [ "Accepts array of widget configs or JSON string", "addAndRemove=true syncs with current widgets", "Existing widgets not in layout are removed", ], float: [ "Float mode allows widgets to move up to fill gaps", "Disable for more predictable layouts", "Can be toggled at runtime", ], column: [ "Changing columns re-layouts existing widgets", "CSS must support the new column count", "Use 'auto' for nested grids", ], }; return notes[operation] || []; } private formatResponse(result: GridStackCodeResult): string { let response = `## GridStack ${result.operation} ${result.description} ### Generated Code: \`\`\`javascript ${result.code} \`\`\``; if (result.parameters && Object.keys(result.parameters).length > 0) { response += ` ### Parameters: \`\`\`json ${JSON.stringify(result.parameters, null, 2)} \`\`\``; } if (result.example) { response += ` ### Example: \`\`\`javascript ${result.example} \`\`\``; } if (result.notes && result.notes.length > 0) { response += ` ### Notes: ${result.notes.map((note) => `- ${note}`).join("\n")}`; } return response; }