Redesign and translate grid controls

This commit is contained in:
2026-07-22 23:19:21 +02:00
parent 17cdeb6174
commit 7257deacdc
4 changed files with 424 additions and 290 deletions
@@ -290,6 +290,9 @@ Implemented layout foundation:
- active sorts and filters are summarized above the grid and can be removed individually or reset together
- column filter buttons show their selected-value count
- filter menus provide value search, select-all/select-none controls and explicit cancel/apply actions
- filter values and visible columns use full-width selectable rows instead of permanent checkbox controls
- column settings use the same searchable panel pattern as column filters
- column headings are visually plain text while remaining keyboard-accessible sort controls
Acceptance criteria:
+131 -13
View File
@@ -115,24 +115,62 @@ body {
margin-top: 2rem;
border: 1px solid #cfd7e5;
background: #fff;
border-radius: 4px;
border-radius: 5px;
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
padding: 0.45rem;
width: 300px;
padding: 0.55rem;
width: 340px;
color: #1f2937;
text-align: left;
}
.column-settings-actions {
.column-settings-title-row,
.column-settings-footer {
display: flex;
justify-content: flex-end;
margin-bottom: 0.35rem;
align-items: center;
justify-content: space-between;
gap: 0.4rem;
}
.column-settings-title-row {
margin-bottom: 0.25rem;
font-size: 0.82rem;
}
.column-settings-close {
border: 0 !important;
background: transparent !important;
color: #4b5563;
padding: 0.05rem 0.2rem !important;
font-size: 1rem !important;
line-height: 1;
cursor: pointer;
}
.column-settings-explanation {
margin-bottom: 0.4rem;
color: #4b5563;
font-size: 0.72rem;
line-height: 1.3;
}
.column-settings-search {
width: 100%;
margin-bottom: 0.4rem;
border: 1px solid #c4cddc;
border-radius: 4px;
padding: 0.3rem 0.4rem;
font-size: 0.76rem;
}
.column-settings-list {
display: flex;
flex-direction: column;
gap: 0.25rem;
max-height: 340px;
gap: 0.15rem;
max-height: 300px;
overflow: auto;
border: 1px solid #e1e6ef;
border-radius: 4px;
padding: 0.25rem;
}
.column-settings-item {
@@ -142,7 +180,12 @@ body {
gap: 0.4rem;
border: 1px solid transparent;
border-radius: 4px;
padding: 0.2rem 0.25rem;
padding: 0.1rem;
}
.column-settings-item.selected {
border-color: #bfdbfe;
background: #eff6ff;
}
.column-settings-item.dragging {
@@ -158,11 +201,32 @@ body {
background: #f7fafc;
}
.column-settings-item label {
.column-visibility-button {
display: flex;
gap: 0.3rem;
align-items: center;
gap: 0.35rem;
flex: 1;
min-width: 0;
border: 0 !important;
background: transparent !important;
padding: 0.2rem 0.25rem !important;
color: inherit;
text-align: left;
font-size: 0.8rem;
cursor: pointer;
}
.column-visibility-button:disabled {
cursor: default;
}
.selection-marker {
display: inline-flex;
flex: 0 0 1rem;
align-items: center;
justify-content: center;
color: #1d4ed8;
font-weight: 700;
}
.column-settings-order {
@@ -189,6 +253,23 @@ body {
scrollbar-gutter: stable;
}
.column-settings-empty {
padding: 0.55rem 0.35rem;
color: #6b7280;
font-size: 0.76rem;
}
.column-settings-footer {
justify-content: flex-end;
margin-top: 0.45rem;
}
.column-settings-footer button.primary {
border-color: #2563eb;
background: #2563eb;
color: #fff;
}
.tree-editor-layout {
display: grid;
grid-template-columns: minmax(240px, 320px) minmax(0, 1fr);
@@ -300,7 +381,6 @@ body {
gap: 0.3rem;
}
.tree-grid .header-sort-btn,
.tree-grid .header-filter-btn {
border: 1px solid #c4cddc;
background: #fff;
@@ -310,7 +390,28 @@ body {
}
.tree-grid .header-sort-btn {
display: inline-flex;
align-items: center;
gap: 0.25rem;
border: 0;
background: transparent;
padding: 0;
color: #1f2937;
font: inherit;
font-size: 0.78rem;
font-weight: 600;
text-align: left;
cursor: pointer;
}
.tree-grid .header-sort-btn:hover span:first-child,
.tree-grid .header-sort-btn:focus-visible span:first-child {
text-decoration: underline;
}
.tree-grid .sort-indicator {
color: #2563eb;
font-size: 0.85rem;
}
.tree-grid .header-filter-btn.active {
@@ -407,9 +508,26 @@ body {
.tree-grid .header-filter-item {
display: flex;
align-items: center;
gap: 0.25rem;
gap: 0.35rem;
width: 100%;
border: 1px solid transparent;
border-radius: 3px;
background: transparent;
padding: 0.25rem 0.3rem;
color: #1f2937;
font-weight: 400;
font-size: 0.75rem;
text-align: left;
cursor: pointer;
}
.tree-grid .header-filter-item:hover {
background: #f3f4f6;
}
.tree-grid .header-filter-item.selected {
border-color: #bfdbfe;
background: #eff6ff;
}
.tree-grid .header-filter-empty {
File diff suppressed because it is too large Load Diff
+31 -31
View File
@@ -55,36 +55,36 @@ export interface ColumnDef {
// BMK stays locked as the first column because circuit identity and circuit drag
// handles depend on an always-visible equipment identifier.
export const allColumns: ColumnDef[] = [
{ key: "equipmentIdentifier", label: "Equipment identifier", defaultVisible: true, locked: true },
{ key: "displayName", label: "Display name", defaultVisible: true },
{ key: "quantity", label: "Quantity", numeric: true, defaultVisible: true },
{ key: "powerPerUnit", label: "Power / unit", numeric: true, defaultVisible: true },
{ key: "simultaneityFactor", label: "Simultaneity", numeric: true, defaultVisible: true },
{ key: "rowTotalPower", label: "Row total", numeric: true, defaultVisible: true },
{ key: "circuitTotalPower", label: "Circuit total", numeric: true, defaultVisible: true },
{ key: "protectionSummary", label: "Protection summary", defaultVisible: true },
{ key: "cableSummary", label: "Cable summary", defaultVisible: true },
{ key: "roomSummary", label: "Room", defaultVisible: true },
{ key: "remark", label: "Remark", defaultVisible: true },
{ key: "technicalName", label: "Technical name" },
{ key: "connectionKind", label: "Connection kind" },
{ key: "phaseType", label: "Phase type" },
{ key: "costGroup", label: "Cost group" },
{ key: "category", label: "Category" },
{ key: "level", label: "Level" },
{ key: "roomNumberSnapshot", label: "Room number" },
{ key: "roomNameSnapshot", label: "Room name" },
{ key: "cosPhi", label: "cosPhi", numeric: true },
{ key: "protectionType", label: "Protection type" },
{ key: "protectionRatedCurrent", label: "Protection rated current", numeric: true },
{ key: "protectionCharacteristic", label: "Protection characteristic" },
{ key: "cableType", label: "Cable type" },
{ key: "cableCrossSection", label: "Cable cross-section" },
{ key: "cableLength", label: "Cable length", numeric: true },
{ key: "rcdAssignment", label: "RCD assignment" },
{ key: "terminalDesignation", label: "Terminal designation" },
{ key: "voltage", label: "Voltage", numeric: true },
{ key: "controlRequirement", label: "Control requirement" },
{ key: "equipmentIdentifier", label: "Betriebsmittelkennzeichen", defaultVisible: true, locked: true },
{ key: "displayName", label: "Anzeigename", defaultVisible: true },
{ key: "quantity", label: "Anzahl", numeric: true, defaultVisible: true },
{ key: "powerPerUnit", label: "Leistung / Gerät", numeric: true, defaultVisible: true },
{ key: "simultaneityFactor", label: "Gleichzeitigkeit", numeric: true, defaultVisible: true },
{ key: "rowTotalPower", label: "Zeilensumme", numeric: true, defaultVisible: true },
{ key: "circuitTotalPower", label: "Stromkreissumme", numeric: true, defaultVisible: true },
{ key: "protectionSummary", label: "Schutz", defaultVisible: true },
{ key: "cableSummary", label: "Kabel", defaultVisible: true },
{ key: "roomSummary", label: "Raum", defaultVisible: true },
{ key: "remark", label: "Bemerkung", defaultVisible: true },
{ key: "technicalName", label: "Technischer Name" },
{ key: "connectionKind", label: "Anschlussart" },
{ key: "phaseType", label: "Phasenart" },
{ key: "costGroup", label: "Kostengruppe" },
{ key: "category", label: "Kategorie" },
{ key: "level", label: "Ebene" },
{ key: "roomNumberSnapshot", label: "Raumnummer" },
{ key: "roomNameSnapshot", label: "Raumname" },
{ key: "cosPhi", label: "cos φ", numeric: true },
{ key: "protectionType", label: "Schutzart" },
{ key: "protectionRatedCurrent", label: "Bemessungsstrom", numeric: true },
{ key: "protectionCharacteristic", label: "Charakteristik" },
{ key: "cableType", label: "Kabeltyp" },
{ key: "cableCrossSection", label: "Kabelquerschnitt" },
{ key: "cableLength", label: "Kabellänge", numeric: true },
{ key: "rcdAssignment", label: "RCD-Zuordnung" },
{ key: "terminalDesignation", label: "Klemmenbezeichnung" },
{ key: "voltage", label: "Spannung", numeric: true },
{ key: "controlRequirement", label: "Steuerungsanforderung" },
{ key: "status", label: "Status" },
{ key: "isReserve", label: "Reserve" },
];
@@ -172,7 +172,7 @@ export function formatValue(value: GridValue): string {
return "-";
}
if (typeof value === "boolean") {
return value ? "Yes" : "No";
return value ? "Ja" : "Nein";
}
return String(value);
}