Standardize phase type labels
This commit is contained in:
@@ -25,6 +25,7 @@ import {
|
||||
buildDeviceRowEditPatch,
|
||||
defaultVisibleColumnKeys,
|
||||
deviceFieldKeys,
|
||||
formatPhaseTypeLabel,
|
||||
formatValue,
|
||||
} from "../utils/circuit-grid-model";
|
||||
import {
|
||||
@@ -153,16 +154,6 @@ function normalizeUiError(err: unknown): string {
|
||||
return message;
|
||||
}
|
||||
|
||||
function formatPhaseTypeLabel(value: string): string {
|
||||
if (value === "three_phase") {
|
||||
return "Dreiphasig";
|
||||
}
|
||||
if (value === "single_phase") {
|
||||
return "Einphasig";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function isPrintableKey(event: KeyboardEvent<HTMLElement>) {
|
||||
return event.key.length === 1 && !event.metaKey && !event.ctrlKey && !event.altKey;
|
||||
}
|
||||
@@ -235,7 +226,7 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
||||
const pendingSelectedDeviceRowIdsAfterReload = useRef<string[] | null>(null);
|
||||
const pendingSelectedCircuitIdsAfterReload = useRef<string[] | null>(null);
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
const inputRef = useRef<HTMLInputElement | HTMLSelectElement | null>(null);
|
||||
const focusTokenRef = useRef(1);
|
||||
const projectRevisionRef = useRef<number | null>(null);
|
||||
|
||||
@@ -449,7 +440,14 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
||||
</button>
|
||||
) : null}
|
||||
{activeColumnFilters.map(({ column, values }) => {
|
||||
const shownValues = values.slice(0, 2).join(", ");
|
||||
const shownValues = values
|
||||
.slice(0, 2)
|
||||
.map((value) =>
|
||||
column.key === "phaseType"
|
||||
? formatPhaseTypeLabel(value)
|
||||
: value
|
||||
)
|
||||
.join(", ");
|
||||
const valueSummary = values.length > 2 ? `${shownValues} +${values.length - 2}` : shownValues;
|
||||
return (
|
||||
<button
|
||||
@@ -1213,9 +1211,12 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
||||
return;
|
||||
}
|
||||
inputRef.current.focus();
|
||||
if (editingCell.mode === "selectExisting") {
|
||||
if (
|
||||
inputRef.current instanceof HTMLInputElement &&
|
||||
editingCell.mode === "selectExisting"
|
||||
) {
|
||||
inputRef.current.select();
|
||||
} else {
|
||||
} else if (inputRef.current instanceof HTMLInputElement) {
|
||||
const len = inputRef.current.value.length;
|
||||
inputRef.current.setSelectionRange(len, len);
|
||||
}
|
||||
@@ -1245,7 +1246,10 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
||||
return;
|
||||
}
|
||||
// Spreadsheet behavior: typing on a selected cell starts overwrite mode.
|
||||
const currentDisplay = mode === "replaceWithTypedChar" ? typedChar ?? "" : String(visibleCell.value ?? "");
|
||||
const currentDisplay =
|
||||
mode === "replaceWithTypedChar" && cell.cellKey !== "phaseType"
|
||||
? typedChar ?? ""
|
||||
: String(visibleCell.value ?? "");
|
||||
focusTokenRef.current += 1;
|
||||
setEditingCell({
|
||||
...cell,
|
||||
@@ -1384,7 +1388,10 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
||||
}
|
||||
|
||||
// Commits the active editor input through command history so edits remain undoable.
|
||||
async function commitEdit(direction: SaveDirection = "stay") {
|
||||
async function commitEdit(
|
||||
direction: SaveDirection = "stay",
|
||||
draftOverride?: string
|
||||
) {
|
||||
if (!editingCell) {
|
||||
return;
|
||||
}
|
||||
@@ -1394,10 +1401,11 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
||||
setEditingCell(null);
|
||||
return;
|
||||
}
|
||||
const draft = draftOverride ?? editingCell.draft;
|
||||
if (
|
||||
editingCell.cellKey === "equipmentIdentifier" &&
|
||||
row.circuit &&
|
||||
hasEquipmentIdentifierConflict(allCircuits, row.circuit.id, editingCell.draft)
|
||||
hasEquipmentIdentifierConflict(allCircuits, row.circuit.id, draft)
|
||||
) {
|
||||
setError("Dieses Betriebsmittelkennzeichen ist bereits vorhanden. Die Änderung wurde nicht gespeichert.");
|
||||
return;
|
||||
@@ -1424,7 +1432,11 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
||||
const command: HistoryCommand = {
|
||||
label: "Aus freier Zeile erstellen",
|
||||
redo: async () => {
|
||||
const selection = await createFromPlaceholder(row.sectionId, editingCell.cellKey, editingCell.draft);
|
||||
const selection = await createFromPlaceholder(
|
||||
row.sectionId,
|
||||
editingCell.cellKey,
|
||||
draft
|
||||
);
|
||||
targetSelection = selection;
|
||||
return nextSelectionIntent();
|
||||
},
|
||||
@@ -1435,7 +1447,7 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
||||
}
|
||||
|
||||
if (cell.kind === "circuitField" && row.circuit) {
|
||||
const next = editingCell.draft;
|
||||
const next = draft;
|
||||
const command: HistoryCommand = {
|
||||
label: "Stromkreisfeld bearbeiten",
|
||||
redo: async () => {
|
||||
@@ -1449,7 +1461,7 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
||||
}
|
||||
|
||||
if (cell.kind === "deviceField" && row.device) {
|
||||
const next = editingCell.draft;
|
||||
const next = draft;
|
||||
const command: HistoryCommand = {
|
||||
label: "Gerätefeld bearbeiten",
|
||||
redo: async () => {
|
||||
@@ -1463,7 +1475,7 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
||||
}
|
||||
|
||||
if (cell.kind === "deviceField" && row.rowType === "reserveCircuit" && row.circuit) {
|
||||
const next = editingCell.draft;
|
||||
const next = draft;
|
||||
const command: HistoryCommand = {
|
||||
label: "Gerätezeile im Reservestromkreis erstellen",
|
||||
redo: async () => {
|
||||
@@ -2816,7 +2828,18 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
||||
</div>
|
||||
<div className="header-filter-values">
|
||||
{(distinctValuesByColumn[column.key] ?? [])
|
||||
.filter((value) => value.toLocaleLowerCase("de-DE").includes(filterValueSearch.trim().toLocaleLowerCase("de-DE")))
|
||||
.filter((value) =>
|
||||
(column.key === "phaseType"
|
||||
? formatPhaseTypeLabel(value)
|
||||
: value
|
||||
)
|
||||
.toLocaleLowerCase("de-DE")
|
||||
.includes(
|
||||
filterValueSearch
|
||||
.trim()
|
||||
.toLocaleLowerCase("de-DE")
|
||||
)
|
||||
)
|
||||
.map((value) => {
|
||||
const selected = filterDraftValues.includes(value);
|
||||
return (
|
||||
@@ -2834,12 +2857,26 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
||||
}
|
||||
>
|
||||
<span className="selection-marker" aria-hidden="true">{selected ? "✓" : ""}</span>
|
||||
<span>{value}</span>
|
||||
<span>
|
||||
{column.key === "phaseType"
|
||||
? formatPhaseTypeLabel(value)
|
||||
: value}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
{(distinctValuesByColumn[column.key] ?? []).filter((value) =>
|
||||
value.toLocaleLowerCase("de-DE").includes(filterValueSearch.trim().toLocaleLowerCase("de-DE"))
|
||||
{(distinctValuesByColumn[column.key] ?? []).filter(
|
||||
(value) =>
|
||||
(column.key === "phaseType"
|
||||
? formatPhaseTypeLabel(value)
|
||||
: value
|
||||
)
|
||||
.toLocaleLowerCase("de-DE")
|
||||
.includes(
|
||||
filterValueSearch
|
||||
.trim()
|
||||
.toLocaleLowerCase("de-DE")
|
||||
)
|
||||
).length === 0 ? (
|
||||
<div className="header-filter-empty">Keine passenden Werte gefunden.</div>
|
||||
) : null}
|
||||
@@ -3375,9 +3412,52 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
||||
}
|
||||
}}
|
||||
>
|
||||
{isEditing ? (
|
||||
{isEditing && column.key === "phaseType" ? (
|
||||
<select
|
||||
ref={(element) => {
|
||||
inputRef.current = element;
|
||||
}}
|
||||
value={editingCell.draft}
|
||||
aria-label="Phasenart auswählen"
|
||||
onChange={(event) =>
|
||||
void commitEdit("stay", event.target.value)
|
||||
}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
void commitEdit("stay");
|
||||
} else if (event.key === "Escape") {
|
||||
event.preventDefault();
|
||||
cancelEdit();
|
||||
} else if (event.key === "Tab") {
|
||||
event.preventDefault();
|
||||
void commitEdit(
|
||||
event.shiftKey ? "prev" : "next"
|
||||
);
|
||||
}
|
||||
}}
|
||||
onBlur={() => {
|
||||
requestAnimationFrame(() => {
|
||||
const active =
|
||||
document.activeElement as HTMLElement | null;
|
||||
if (
|
||||
!active ||
|
||||
!containerRef.current?.contains(active)
|
||||
) {
|
||||
setEditingCell(null);
|
||||
focusGridWithoutScroll();
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<option value="single_phase">1-phasig</option>
|
||||
<option value="three_phase">3-phasig</option>
|
||||
</select>
|
||||
) : isEditing ? (
|
||||
<input
|
||||
ref={inputRef}
|
||||
ref={(element) => {
|
||||
inputRef.current = element;
|
||||
}}
|
||||
value={editingCell.draft}
|
||||
aria-invalid={hasDraftIdentifierConflict}
|
||||
title={hasDraftIdentifierConflict ? "Dieses Betriebsmittelkennzeichen ist bereits vorhanden." : undefined}
|
||||
|
||||
Reference in New Issue
Block a user