Version 1.3

TODO:
- Define an Array of Block Size Options: Create an array of available block size options.
- Restrict Blocks from Being Resized: Set block properties to prevent resizing.
- Update the Logic for Handling Layout Changes and Adding Blocks: Modify these functions to account for the size of
  blocks and ensure that the maximum number of allowed blocks is calculated accurately based on their dimensions.



import React, { useState } from 'react';
import { WidthProvider, Responsive } from 'react-grid-layout';
import 'react-grid-layout/css/styles.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import './App.css'; // Import your custom styles

const ResponsiveGridLayout = WidthProvider(Responsive);

const App = () => {
    const [blocks, setBlocks] = useState([]);
    const [newBlockCount, setNewBlockCount] = useState(0);
    const [layout, setLayout] = useState([]);
    const [editingBlock, setEditingBlock] = useState(null);

    const ROW_HEIGHT = 100;
    const GRID_COLS = 4;
    const GRID_ROWS = 6;
    const MAX_BLOCKS = GRID_COLS * GRID_ROWS;

    const handleAddBlock = (width = 1, height = 1) => {
        if (blocks.length >= MAX_BLOCKS) {
            alert("Maximum number of blocks reached!");
            return;
        }

        const newId = `block-${newBlockCount}`;
        let availablePosition = null;

        for (let y = 0; y < GRID_ROWS; y++) {
            for (let x = 0; x < GRID_COLS; x++) {
                // Check if this space is available for the new block of given width and height
                let canPlace = true;

                // Ensure the new block fits within the grid's boundaries
                if (x + width > GRID_COLS || y + height > GRID_ROWS) {
                    canPlace = false;
                    continue;
                }

                // Check for overlapping blocks
                for (let dy = 0; dy < height; dy++) {
                    for (let dx = 0; dx < width; dx++) {
                        if (blocks.some(block => block.x === x + dx && block.y === y + dy)) {
                            canPlace = false;
                            break;
                        }
                    }
                    if (!canPlace) break;
                }

                if (canPlace) {
                    availablePosition = { x, y, w: width, h: height };
                    break;
                }
            }
            if (availablePosition) break; // Found an available position
        }

        if (availablePosition) {
            setBlocks(prevBlocks => [
                ...prevBlocks,
                { i: newId, x: availablePosition.x, y: availablePosition.y, w: width, h: height, content: 'New Block' }
            ]);
            setNewBlockCount(prevCount => prevCount + 1);
            setLayout(prevLayout => [...prevLayout, { i: newId, ...availablePosition }]);
        } else {
            alert("No available space to add a new block!");
        }
    };

    const handleRemoveBlock = (id) => {
        setBlocks(prevBlocks => prevBlocks.filter(block => block.i !== id));
        setLayout(prevLayout => prevLayout.filter(item => item.i !== id));
    };

    const handleEditBlock = (block) => {
        setEditingBlock(block);
    };

    const handleSaveBlockContent = (newContent) => {
        if (!editingBlock) return;

        setBlocks(prevBlocks =>
            prevBlocks.map(block => (block.i === editingBlock.i ? { ...block, content: newContent } : block))
        );
        setEditingBlock(null);
    };

    const handleLayoutChange = (newLayout) => {
        const updatedLayout = newLayout.map(item => {
            // Ensure y position does not exceed GRID_ROWS
            if (item.y >= GRID_ROWS) {
                item.y = GRID_ROWS - 1; // Keep it at the maximum row
            }
            return item;
        });

        // Update blocks based on the new layout
        const updatedBlocks = blocks.map(block => {
            const layoutItem = updatedLayout.find(item => item.i === block.i);
            if (layoutItem) {
                return { ...block, x: layoutItem.x, y: layoutItem.y, w: layoutItem.w, h: layoutItem.h };
            }
            return block;
        });

        // Check for collisions and resolve them by finding new available positions
        updatedBlocks.forEach(block => {
            if (block.y >= GRID_ROWS) {
                // Find the next available position downwards
                let foundPosition = false;
                for (let newY = 0; newY < GRID_ROWS; newY++) {
                    if (!updatedBlocks.some(existingBlock =>
                        existingBlock.x === block.x && existingBlock.y === newY)) {
                        block.y = newY; // Assign to the first empty space found
                        foundPosition = true;
                        break;
                    }
                }

                if (!foundPosition) {
                    block.y = GRID_ROWS - 1; // Placing it at the last row if no positions are free
                }
            }
        });

        setLayout(updatedLayout);
        setBlocks(updatedBlocks);
    };

    const renderBlockContent = (block) => (
        <div className="flex flex-col h-full justify-between">
            <div className="p-2 text-sm">{block.content}</div>
            <div className="flex justify-end p-2 space-x-2">
                <button className="btn btn-secondary btn-sm" onClick={() => handleEditBlock(block)}>Edit</button>
                <button className="btn btn-danger btn-sm" onClick={() => handleRemoveBlock(block.i)}>Remove</button>
            </div>
        </div>
    );

    return (
        <div className="container-fluid">
            <h1>Tetris-Like Layout Editor</h1>
            {/* Example usage of handleAddBlock with random sizes: */}
            <button className="btn btn-primary mb-3" onClick={() => handleAddBlock(1, 1)}>Add 1x1 Block</button>
            <button className="btn btn-primary mb-3" onClick={() => handleAddBlock(2, 1)}>Add 2x1 Block</button>
            <button className="btn btn-primary mb-3" onClick={() => handleAddBlock(1, 2)}>Add 1x2 Block</button>

            <ResponsiveGridLayout
                className="layout"
                layouts={{ lg: layout }}
                breakpoints={{ lg: 1200 }}
                cols={{ lg: GRID_COLS }}
                rowHeight={ROW_HEIGHT}
                onLayoutChange={handleLayoutChange}
                isBounded={false}
                preventCollision={true} // Prevent collisions when dragging
            >
                {blocks.map((block) => (
                    <div key={block.i} className="block" data-grid={block}>
                        <div className="content-area">
                            <div className="drag-handle" style={{ cursor: 'move' }}></div>
                            {renderBlockContent(block)}
                        </div>
                    </div>
                ))}
            </ResponsiveGridLayout>
        </div>
    );
};

export default App;