Bugfix editor
This commit is contained in:
@@ -61,6 +61,8 @@ interface EditingCell extends SelectedCell {
|
|||||||
draft: string;
|
draft: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SaveDirection = "stay" | "next" | "prev";
|
||||||
|
|
||||||
const columns: Array<{ key: CellKey; label: string; numeric?: boolean }> = [
|
const columns: Array<{ key: CellKey; label: string; numeric?: boolean }> = [
|
||||||
{ key: "equipmentIdentifier", label: "Equipment identifier" },
|
{ key: "equipmentIdentifier", label: "Equipment identifier" },
|
||||||
{ key: "name", label: "Name" },
|
{ key: "name", label: "Name" },
|
||||||
@@ -228,6 +230,17 @@ function parseNumeric(cellKey: CellKey, draft: string): number | undefined {
|
|||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeUiError(err: unknown): string {
|
||||||
|
const message = err instanceof Error ? err.message : "Operation failed.";
|
||||||
|
if (message.includes("Duplicate equipmentIdentifier")) {
|
||||||
|
return "Das Betriebsmittelkennzeichen ist in dieser Stromkreisliste bereits vorhanden.";
|
||||||
|
}
|
||||||
|
if (message.includes("Invalid number")) {
|
||||||
|
return "Bitte einen gültigen Zahlenwert eingeben.";
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
export function CircuitTreeEditor(props: { projectId: string; circuitListId: string }) {
|
export function CircuitTreeEditor(props: { projectId: string; circuitListId: string }) {
|
||||||
const { projectId, circuitListId } = props;
|
const { projectId, circuitListId } = props;
|
||||||
const [data, setData] = useState<CircuitTreeResponseDto | null>(null);
|
const [data, setData] = useState<CircuitTreeResponseDto | null>(null);
|
||||||
@@ -238,7 +251,9 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
const [activeSectionId, setActiveSectionId] = useState<string | null>(null);
|
const [activeSectionId, setActiveSectionId] = useState<string | null>(null);
|
||||||
const [isSaving, setIsSaving] = useState(false);
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
const [pendingFocus, setPendingFocus] = useState<SelectedCell | null>(null);
|
const [pendingFocus, setPendingFocus] = useState<SelectedCell | null>(null);
|
||||||
|
const pendingSelectionAfterReload = useRef<SelectedCell | null>(null);
|
||||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
async function loadTree() {
|
async function loadTree() {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
@@ -246,10 +261,15 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
try {
|
try {
|
||||||
const tree = await getCircuitTree(projectId, circuitListId);
|
const tree = await getCircuitTree(projectId, circuitListId);
|
||||||
setData(tree);
|
setData(tree);
|
||||||
|
if (pendingSelectionAfterReload.current) {
|
||||||
|
setSelectedCell(pendingSelectionAfterReload.current);
|
||||||
|
pendingSelectionAfterReload.current = null;
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : "Could not load circuit tree.");
|
setError(normalizeUiError(err));
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
requestAnimationFrame(() => containerRef.current?.focus());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,10 +330,21 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setSelectedCell(pendingFocus);
|
setSelectedCell(pendingFocus);
|
||||||
setEditingCell({ ...pendingFocus, draft: "" });
|
startEdit(pendingFocus);
|
||||||
setPendingFocus(null);
|
setPendingFocus(null);
|
||||||
|
requestAnimationFrame(() => containerRef.current?.focus());
|
||||||
}, [pendingFocus]);
|
}, [pendingFocus]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!editingCell) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
inputRef.current?.focus();
|
||||||
|
inputRef.current?.select();
|
||||||
|
});
|
||||||
|
}, [editingCell]);
|
||||||
|
|
||||||
const editableCells = useMemo(() => {
|
const editableCells = useMemo(() => {
|
||||||
const cells: SelectedCell[] = [];
|
const cells: SelectedCell[] = [];
|
||||||
for (const row of gridRows) {
|
for (const row of gridRows) {
|
||||||
@@ -326,6 +357,22 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
return cells;
|
return cells;
|
||||||
}, [gridRows]);
|
}, [gridRows]);
|
||||||
|
|
||||||
|
const rowEditableCells = useMemo(() => {
|
||||||
|
const map = new Map<string, CellKey[]>();
|
||||||
|
for (const row of gridRows) {
|
||||||
|
const keys = columns.map((col) => col.key).filter((key) => canEditCell(row, key));
|
||||||
|
if (keys.length > 0) {
|
||||||
|
map.set(row.rowKey, keys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}, [gridRows]);
|
||||||
|
|
||||||
|
const editableRowOrder = useMemo(
|
||||||
|
() => gridRows.filter((row) => rowEditableCells.has(row.rowKey)).map((row) => row.rowKey),
|
||||||
|
[gridRows, rowEditableCells]
|
||||||
|
);
|
||||||
|
|
||||||
function findRow(rowKey: string) {
|
function findRow(rowKey: string) {
|
||||||
return gridRows.find((row) => row.rowKey === rowKey);
|
return gridRows.find((row) => row.rowKey === rowKey);
|
||||||
}
|
}
|
||||||
@@ -337,9 +384,12 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
if (cellKey === "rowTotalPower" || cellKey === "circuitTotalPower") {
|
if (cellKey === "rowTotalPower" || cellKey === "circuitTotalPower") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (row.kind === "summary" || row.kind === "reserve") {
|
if (row.kind === "summary") {
|
||||||
return isCircuitField(cellKey);
|
return isCircuitField(cellKey);
|
||||||
}
|
}
|
||||||
|
if (row.kind === "reserve") {
|
||||||
|
return isCircuitField(cellKey) || isDeviceField(cellKey);
|
||||||
|
}
|
||||||
if (row.kind === "device") {
|
if (row.kind === "device") {
|
||||||
return isDeviceField(cellKey);
|
return isDeviceField(cellKey);
|
||||||
}
|
}
|
||||||
@@ -378,24 +428,57 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
setEditingCell({ ...cell, draft: value === "-" ? "" : value });
|
setEditingCell({ ...cell, draft: value === "-" ? "" : value });
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveSelection(offset: number) {
|
function moveHorizontal(direction: 1 | -1) {
|
||||||
if (!selectedCell) {
|
if (!selectedCell) {
|
||||||
if (editableCells.length > 0) {
|
if (editableCells.length) {
|
||||||
setSelectedCell(editableCells[0]);
|
setSelectedCell(editableCells[0]);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const index = editableCells.findIndex(
|
const keys = rowEditableCells.get(selectedCell.rowKey);
|
||||||
(cell) => cell.rowKey === selectedCell.rowKey && cell.cellKey === selectedCell.cellKey
|
if (!keys || !keys.length) {
|
||||||
);
|
|
||||||
if (index < 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const nextIndex = Math.min(editableCells.length - 1, Math.max(0, index + offset));
|
const currentIndex = keys.indexOf(selectedCell.cellKey);
|
||||||
setSelectedCell(editableCells[nextIndex]);
|
const nextIndex = Math.min(keys.length - 1, Math.max(0, currentIndex + direction));
|
||||||
|
setSelectedCell({ rowKey: selectedCell.rowKey, cellKey: keys[nextIndex] });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveEditingCell(nextCell?: SelectedCell | null) {
|
function moveVertical(direction: 1 | -1) {
|
||||||
|
if (!selectedCell) {
|
||||||
|
if (editableCells.length) {
|
||||||
|
setSelectedCell(editableCells[0]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const rowIndex = editableRowOrder.indexOf(selectedCell.rowKey);
|
||||||
|
if (rowIndex < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const targetRowIndex = rowIndex + direction;
|
||||||
|
if (targetRowIndex < 0 || targetRowIndex >= editableRowOrder.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const targetRowKey = editableRowOrder[targetRowIndex];
|
||||||
|
const targetKeys = rowEditableCells.get(targetRowKey) ?? [];
|
||||||
|
if (!targetKeys.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const preferredColIndex = columns.findIndex((column) => column.key === selectedCell.cellKey);
|
||||||
|
let best = targetKeys[0];
|
||||||
|
let bestDistance = Number.POSITIVE_INFINITY;
|
||||||
|
for (const key of targetKeys) {
|
||||||
|
const idx = columns.findIndex((column) => column.key === key);
|
||||||
|
const distance = Math.abs(idx - preferredColIndex);
|
||||||
|
if (distance < bestDistance) {
|
||||||
|
bestDistance = distance;
|
||||||
|
best = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setSelectedCell({ rowKey: targetRowKey, cellKey: best });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveEditingCell(direction: SaveDirection = "stay") {
|
||||||
if (!editingCell) {
|
if (!editingCell) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -409,9 +492,22 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
setError(null);
|
setError(null);
|
||||||
const key = editingCell.cellKey;
|
const key = editingCell.cellKey;
|
||||||
const draft = editingCell.draft;
|
const draft = editingCell.draft;
|
||||||
|
let nextSelection: SelectedCell | null = { rowKey: editingCell.rowKey, cellKey: editingCell.cellKey };
|
||||||
|
|
||||||
if ((row.kind === "summary" || row.kind === "reserve") && isCircuitField(key)) {
|
if ((row.kind === "summary" || row.kind === "reserve") && isCircuitField(key)) {
|
||||||
await patchCircuit(row.circuit.id, key, draft);
|
await patchCircuit(row.circuit.id, key, draft);
|
||||||
|
} else if (row.kind === "reserve" && isDeviceField(key)) {
|
||||||
|
const created = (await createCircuitDeviceRow(row.circuit.id, {
|
||||||
|
name: "Reserve load",
|
||||||
|
displayName: "Reserve load",
|
||||||
|
phaseType: "single_phase",
|
||||||
|
quantity: 1,
|
||||||
|
powerPerUnit: 0,
|
||||||
|
simultaneityFactor: 1,
|
||||||
|
cosPhi: 1,
|
||||||
|
})) as { id: string };
|
||||||
|
await patchDeviceRow(created.id, key, draft);
|
||||||
|
nextSelection = { rowKey: `compact:${row.circuit.id}`, cellKey: key };
|
||||||
} else if (row.kind === "device" && row.device && isDeviceField(key)) {
|
} else if (row.kind === "device" && row.device && isDeviceField(key)) {
|
||||||
await patchDeviceRow(row.device.id, key, draft);
|
await patchDeviceRow(row.device.id, key, draft);
|
||||||
} else if (row.kind === "compact") {
|
} else if (row.kind === "compact") {
|
||||||
@@ -422,13 +518,25 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setEditingCell(null);
|
if (direction !== "stay" && nextSelection) {
|
||||||
await loadTree();
|
const idx = editableCells.findIndex(
|
||||||
if (nextCell) {
|
(cell) => cell.rowKey === nextSelection!.rowKey && cell.cellKey === nextSelection!.cellKey
|
||||||
setSelectedCell(nextCell);
|
);
|
||||||
|
if (idx >= 0) {
|
||||||
|
const targetIdx =
|
||||||
|
direction === "next"
|
||||||
|
? Math.min(editableCells.length - 1, idx + 1)
|
||||||
|
: Math.max(0, idx - 1);
|
||||||
|
nextSelection = editableCells[targetIdx];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setEditingCell(null);
|
||||||
|
pendingSelectionAfterReload.current = nextSelection;
|
||||||
|
await loadTree();
|
||||||
|
requestAnimationFrame(() => containerRef.current?.focus());
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : "Save failed.");
|
setError(normalizeUiError(err));
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
@@ -478,8 +586,9 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
});
|
});
|
||||||
await loadTree();
|
await loadTree();
|
||||||
setActiveSectionId(sectionId);
|
setActiveSectionId(sectionId);
|
||||||
|
requestAnimationFrame(() => containerRef.current?.focus());
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : "Failed to add reserve circuit.");
|
setError(normalizeUiError(err));
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
@@ -505,7 +614,7 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
cellKey: "displayName",
|
cellKey: "displayName",
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : "Failed to add device row.");
|
setError(normalizeUiError(err));
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
@@ -521,7 +630,7 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
await deleteCircuitDeviceRowById(rowId);
|
await deleteCircuitDeviceRowById(rowId);
|
||||||
await loadTree();
|
await loadTree();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : "Failed to delete device row.");
|
setError(normalizeUiError(err));
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
@@ -537,7 +646,7 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
await deleteCircuitById(circuitId);
|
await deleteCircuitById(circuitId);
|
||||||
await loadTree();
|
await loadTree();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : "Failed to delete circuit.");
|
setError(normalizeUiError(err));
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
@@ -553,7 +662,7 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
await renumberCircuitSection(sectionId);
|
await renumberCircuitSection(sectionId);
|
||||||
await loadTree();
|
await loadTree();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : "Failed to renumber section.");
|
setError(normalizeUiError(err));
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
@@ -561,6 +670,9 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
|
|
||||||
function handleKeyDown(event: KeyboardEvent<HTMLDivElement>) {
|
function handleKeyDown(event: KeyboardEvent<HTMLDivElement>) {
|
||||||
if (editingCell) {
|
if (editingCell) {
|
||||||
|
if (event.key === "Tab") {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const isCtrlPlus =
|
const isCtrlPlus =
|
||||||
@@ -576,16 +688,16 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
}
|
}
|
||||||
if (event.key === "ArrowRight") {
|
if (event.key === "ArrowRight") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
moveSelection(1);
|
moveHorizontal(1);
|
||||||
} else if (event.key === "ArrowLeft") {
|
} else if (event.key === "ArrowLeft") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
moveSelection(-1);
|
moveHorizontal(-1);
|
||||||
} else if (event.key === "ArrowDown") {
|
} else if (event.key === "ArrowDown") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
moveSelection(1);
|
moveVertical(1);
|
||||||
} else if (event.key === "ArrowUp") {
|
} else if (event.key === "ArrowUp") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
moveSelection(-1);
|
moveVertical(-1);
|
||||||
} else if (event.key === "Enter" || event.key === "F2") {
|
} else if (event.key === "Enter" || event.key === "F2") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (selectedCell) {
|
if (selectedCell) {
|
||||||
@@ -599,6 +711,21 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleContainerFocus() {
|
||||||
|
if (editingCell) {
|
||||||
|
const row = findRow(editingCell.rowKey);
|
||||||
|
if (!row || !canEditCell(row, editingCell.cellKey)) {
|
||||||
|
setEditingCell(null);
|
||||||
|
} else {
|
||||||
|
requestAnimationFrame(() => inputRef.current?.focus());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!selectedCell && editableCells.length > 0) {
|
||||||
|
setSelectedCell(editableCells[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return <div className="notice info">Loading circuit tree editor...</div>;
|
return <div className="notice info">Loading circuit tree editor...</div>;
|
||||||
}
|
}
|
||||||
@@ -612,7 +739,13 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
return (
|
return (
|
||||||
<div className="tree-editor-shell">
|
<div className="tree-editor-shell">
|
||||||
{isSaving ? <div className="notice info">Saving...</div> : null}
|
{isSaving ? <div className="notice info">Saving...</div> : null}
|
||||||
<div className="tree-grid-wrap" ref={containerRef} tabIndex={0} onKeyDown={handleKeyDown}>
|
<div
|
||||||
|
className="tree-grid-wrap"
|
||||||
|
ref={containerRef}
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onFocus={handleContainerFocus}
|
||||||
|
>
|
||||||
<table className="tree-grid">
|
<table className="tree-grid">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -673,11 +806,12 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
key={column.key}
|
key={column.key}
|
||||||
className={`${column.numeric ? "num" : ""} ${selected ? "cell-selected" : ""} ${editable ? "cell-editable" : ""}`}
|
className={`${column.numeric ? "num" : ""} ${selected ? "cell-selected" : ""} ${editable ? "cell-editable" : ""}`}
|
||||||
onClick={() => setSelectedCell({ rowKey: row.rowKey, cellKey: column.key })}
|
onClick={() => setSelectedCell({ rowKey: row.rowKey, cellKey: column.key })}
|
||||||
|
onClickCapture={() => requestAnimationFrame(() => containerRef.current?.focus())}
|
||||||
onDoubleClick={() => startEdit({ rowKey: row.rowKey, cellKey: column.key })}
|
onDoubleClick={() => startEdit({ rowKey: row.rowKey, cellKey: column.key })}
|
||||||
>
|
>
|
||||||
{editing ? (
|
{editing ? (
|
||||||
<input
|
<input
|
||||||
autoFocus
|
ref={inputRef}
|
||||||
value={editingCell.draft}
|
value={editingCell.draft}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
setEditingCell((current) =>
|
setEditingCell((current) =>
|
||||||
@@ -687,27 +821,31 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
onKeyDown={(event) => {
|
onKeyDown={(event) => {
|
||||||
if (event.key === "Enter") {
|
if (event.key === "Enter") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
void saveEditingCell(selectedCell);
|
void saveEditingCell("stay");
|
||||||
} else if (event.key === "Escape") {
|
} else if (event.key === "Escape") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
setEditingCell(null);
|
setEditingCell(null);
|
||||||
|
setSelectedCell({ rowKey: row.rowKey, cellKey: column.key });
|
||||||
|
requestAnimationFrame(() => containerRef.current?.focus());
|
||||||
} else if (event.key === "Tab") {
|
} else if (event.key === "Tab") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const idx = editableCells.findIndex(
|
void saveEditingCell(event.shiftKey ? "prev" : "next");
|
||||||
(cell) =>
|
|
||||||
cell.rowKey === editingCell.rowKey && cell.cellKey === editingCell.cellKey
|
|
||||||
);
|
|
||||||
const next =
|
|
||||||
idx >= 0
|
|
||||||
? editableCells[
|
|
||||||
event.shiftKey
|
|
||||||
? Math.max(0, idx - 1)
|
|
||||||
: Math.min(editableCells.length - 1, idx + 1)
|
|
||||||
]
|
|
||||||
: null;
|
|
||||||
void saveEditingCell(next);
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
onBlur={() => {
|
||||||
|
if (!editingCell) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
const active = document.activeElement as HTMLElement | null;
|
||||||
|
const insideEditor =
|
||||||
|
!!active && !!containerRef.current && containerRef.current.contains(active);
|
||||||
|
if (!insideEditor) {
|
||||||
|
setEditingCell(null);
|
||||||
|
requestAnimationFrame(() => containerRef.current?.focus());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
formatValue(value)
|
formatValue(value)
|
||||||
@@ -718,16 +856,28 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
<td className="action-cell">
|
<td className="action-cell">
|
||||||
{row.circuit && row.kind !== "device" ? (
|
{row.circuit && row.kind !== "device" ? (
|
||||||
<>
|
<>
|
||||||
<button type="button" onClick={() => void handleAddManualDevice(row.circuit!, row.sectionId)}>
|
<button
|
||||||
|
type="button"
|
||||||
|
tabIndex={editingCell ? -1 : 0}
|
||||||
|
onClick={() => void handleAddManualDevice(row.circuit!, row.sectionId)}
|
||||||
|
>
|
||||||
Add manual device
|
Add manual device
|
||||||
</button>
|
</button>
|
||||||
<button type="button" onClick={() => void handleDeleteCircuit(row.circuit!.id)}>
|
<button
|
||||||
|
type="button"
|
||||||
|
tabIndex={editingCell ? -1 : 0}
|
||||||
|
onClick={() => void handleDeleteCircuit(row.circuit!.id)}
|
||||||
|
>
|
||||||
Delete circuit
|
Delete circuit
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
{row.device ? (
|
{row.device ? (
|
||||||
<button type="button" onClick={() => void handleDeleteDevice(row.device!.id)}>
|
<button
|
||||||
|
type="button"
|
||||||
|
tabIndex={editingCell ? -1 : 0}
|
||||||
|
onClick={() => void handleDeleteDevice(row.device!.id)}
|
||||||
|
>
|
||||||
Delete device
|
Delete device
|
||||||
</button>
|
</button>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
Reference in New Issue
Block a user