Make Ctrl+Plus context aware

This commit is contained in:
2026-07-22 20:15:33 +02:00
parent fd62bf2e15
commit a6b83285eb
5 changed files with 243 additions and 16 deletions
+74 -14
View File
@@ -5,6 +5,10 @@ import {
inferProjectDeviceSectionKey,
isProjectDevicePlacementValid,
} from "../../domain/services/project-device-placement.service";
import {
getInsertionSortOrder,
resolveGridInsertionIntent,
} from "../utils/circuit-grid-insertion";
import {
createCircuit,
createCircuitDeviceRow,
@@ -1616,7 +1620,7 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
requestAnimationFrame(() => containerRef.current?.focus());
}
async function handleAddReserveCircuit(sectionId: string) {
async function handleAddReserveCircuit(sectionId: string, afterCircuitId?: string) {
const section = data?.sections.find((entry) => entry.id === sectionId);
if (!section) {
return;
@@ -1626,8 +1630,7 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
label: "Add circuit",
redo: async () => {
const next = await getNextCircuitIdentifier(sectionId);
const sortOrder =
section.circuits.length > 0 ? Math.max(...section.circuits.map((circuit) => circuit.sortOrder)) + 10 : 10;
const sortOrder = getInsertionSortOrder(section.circuits, afterCircuitId);
const created = (await createCircuit(projectId, circuitListId, {
sectionId,
equipmentIdentifier: next.nextIdentifier,
@@ -1659,7 +1662,13 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
});
}
async function handleAddManualDevice(circuit: CircuitTreeCircuitDto, sectionId: string) {
async function handleAddManualDevice(
circuit: CircuitTreeCircuitDto,
sectionId: string,
afterDeviceRowId?: string
) {
const originalDeviceCount = circuit.deviceRows.length;
const section = data?.sections.find((entry) => entry.id === sectionId);
let createdRowId: string | null = null;
await runCommand({
label: "Add manual device",
@@ -1667,18 +1676,19 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
const created = (await createCircuitDeviceRow(circuit.id, {
name: "Manual device",
displayName: "Manual device",
phaseType: "single_phase",
phaseType: section?.key === "three_phase" ? "three_phase" : "single_phase",
quantity: 1,
powerPerUnit: 0,
simultaneityFactor: 1,
cosPhi: 1,
sortOrder: getInsertionSortOrder(circuit.deviceRows, afterDeviceRowId),
})) as { id: string };
createdRowId = created.id;
setActiveSectionId(sectionId);
return {
rowKey: `device:${created.id}`,
rowKey: originalDeviceCount === 0 ? `circuitCompact:${circuit.id}` : `device:${created.id}`,
cellKey: "displayName",
rowType: "deviceRow",
rowType: originalDeviceCount === 0 ? "circuitCompact" : "deviceRow",
sectionId,
circuitId: circuit.id,
deviceId: created.id,
@@ -1688,17 +1698,68 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
if (createdRowId) {
await deleteCircuitDeviceRowById(createdRowId);
}
const rowKey =
originalDeviceCount === 0
? `reserveCircuit:${circuit.id}`
: originalDeviceCount === 1
? `circuitCompact:${circuit.id}`
: afterDeviceRowId
? `device:${afterDeviceRowId}`
: `circuitSummary:${circuit.id}`;
return {
rowKey: `reserveCircuit:${circuit.id}`,
rowKey,
cellKey: "displayName",
rowType: "reserveCircuit",
rowType:
originalDeviceCount === 0
? "reserveCircuit"
: originalDeviceCount === 1
? "circuitCompact"
: afterDeviceRowId
? "deviceRow"
: "circuitSummary",
sectionId,
circuitId: circuit.id,
deviceId: afterDeviceRowId,
};
},
});
}
async function handleInsertBelowSelection() {
const row = selectedCell ? findRow(selectedCell.rowKey) : null;
const cell = row?.cells.find((entry) => entry.cellKey === selectedCell?.cellKey);
const selection =
row && cell && row.rowType !== "section"
? {
rowType: row.rowType,
cellKind: cell.kind,
sectionId: row.sectionId,
circuitId: row.circuit?.id,
deviceId: row.device?.id,
}
: null;
const intent = resolveGridInsertionIntent(
selection,
activeSectionId ?? data?.sections[0]?.id
);
if (!intent) {
setError("No section is available for insertion.");
return;
}
if (intent.kind === "circuit") {
await handleAddReserveCircuit(intent.sectionId, intent.afterCircuitId);
return;
}
const circuit = data?.sections
.flatMap((sectionEntry) => sectionEntry.circuits)
.find((entry) => entry.id === intent.circuitId);
if (!circuit) {
setError("Invalid circuit id.");
return;
}
await handleAddManualDevice(circuit, intent.sectionId, intent.afterDeviceRowId);
}
function resolvePhaseType(device: ProjectDeviceDto): string {
return device.phaseType;
}
@@ -2472,10 +2533,7 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
(event.key === "+" || (event.shiftKey && event.key === "=") || event.code === "NumpadAdd");
if (isCtrlPlus) {
event.preventDefault();
const sectionId = activeSectionId ?? data?.sections[0]?.id;
if (sectionId) {
void handleAddReserveCircuit(sectionId);
}
void handleInsertBelowSelection();
return;
}
if (event.key === "Tab" && selectedCell) {
@@ -3486,7 +3544,9 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
</table>
</div>
</div>
<p className="todo-hint">TODO Phase: Ctrl+Plus currently adds circuit at end of active section.</p>
<p className="todo-hint">
Ctrl+Plus inserts below the selected circuit or device context. Use Undo to revert the insertion.
</p>
</div>
);
}