Placing a component spanning multiple cells from a drag selection
constraint: The editor is rendered server-side by Livewire, but the drag selection happens in the browser. Sending one event per pointer move would flood the server; sending only the final selection means Livewire must derive row/col/colspan from two cell coordinates.
Alpine.js tracks the drag locally and dispatches a single Livewire event with selectionStart + selectionEnd when the pointer is released. PageEditor::addComponentToCellFromAlpine() derives row/col/colspan from min/max of the two points and persists a PageComponent row with the grid_position as JSON.
public function addComponentToCellFromAlpine($type, $selectionStart, $selectionEnd)
{
$row = min($selectionStart['row'], $selectionEnd['row']);
$col = min($selectionStart['col'], $selectionEnd['col']);
$colEnd = max($selectionStart['col'], $selectionEnd['col']);
$colspan = ($colEnd - $col) + 1;
PageComponent::create([
'page_id' => $this->page->id,
'type' => $type,
'settings' => [
'grid_position' => [
'row' => $row, 'col' => $col,
'rowspan' => 1, 'colspan' => $colspan,
],
],
]);
}